數據結構中按邏輯結構分爲4類:集合、線性表、樹、圖。按存儲結構分爲2類:順序存儲、鏈式存儲。
一些輔助定義:
#define MAXSIZE 20
#define OK 0
#define ERROR 1
typedef int Status;
typedef int ElemType;
//順序存儲
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
//鏈式存儲節點
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*PNode;
//靜態鏈式線性表
typedef struct {
ElemType data;
int cur;
}Component,StaticLinkList[MAXSIZE];
所有代碼都是可運行的,爲方便測試,現附上main()函數。
int main(int argc, const char * argv[])
{
PNode pNode = NULL;
PNode _pNode = NULL;
Status ret = ERROR;
ret = CreatLinkList(&pNode, MAXSIZE);
if (ret) {
printf("創建鏈表失敗!\n");
return ERROR;
}
ret = CreatLinkList(&_pNode, MAXSIZE);
if (ret) {
printf("創建鏈表失敗!\n");
return ERROR;
}
// PrintLinkList(pNode);
// ReverseLinkList(&pNode);
// PrintLinkList(_pNode);
// UnionLinkList(&pNode, _pNode);
// PrintLinkList(pNode);
PNode entryNode = NULL;
PNode *mtpNode = (PNode *)malloc(sizeof(Node));
ret = IsCircleLinkList(pNode, mtpNode);
if (!ret) {
printf("該鏈表無環!\n");
}else{
entryNode = FindEntryNode(pNode, (*mtpNode));
printf("該鏈表有環!\n環節點的值爲:%d\n",entryNode->data);
}
return 0;
}
1.線性表的順序存儲
元素插入
Status ListInsert(SqList *L, int i, ElemType e)
{
i -= 1;
if (L == NULL || L->length<i || i < 0 || L->length >= MAXSIZE) {
printf("鏈表爲空,或者插入位置非法!\n");
return ERROR;
}
for (int j = L->length-1; j >= i; j--) {
L->data[j+1] = L->data[j];
}
L->data[i] = e;
L->length += 1;
return OK;
}
Status ListDelete(SqList *L, int i, ElemType *e)
{
if (L == NULL || i < 1 || i > L->length) {
printf("鏈表爲空,或者刪除位置溢出!");
return ERROR;
}
*e = L->data[i -1];
for (int j = i; j < L->length; j++) {
L->data[j-1] = L->data[j];
}
L->length -= 1;
return OK;
}
2.線性表的鏈式存儲(有兩種實現方式(動態鏈表/靜態鏈表),其中靜態鏈表用數組實現,數組下標相當於動態鏈表中的指針域)
//動態鏈表
創建鏈表
Status CreatLinkList(PNode *ppNode, int n)
{
if (n < 1) {
printf("參數錯誤!\n");
return ERROR;
}
*ppNode = (PNode)malloc(sizeof(Node));//頭結點
if (!(*ppNode)) {
printf("內存分配失敗!\n");
return ERROR;
}
(*ppNode)->data = 0;
(*ppNode)->next = NULL;
int i = 0;
PNode _ppNode = NULL;
PNode head_pNode = (*ppNode);
for (i = 0; i < n; i++) {
_ppNode = (PNode)malloc(sizeof(Node));
if (_ppNode) {
_ppNode->data = i;
_ppNode->next = NULL;
head_pNode->next = _ppNode;
head_pNode = head_pNode->next;
}else{
printf("內存分配失敗!\n");
return ERROR;
}
}
(*ppNode)->data = i;
return OK;
}
void PrintLinkList(PNode pNode)
{
PNode _ppNode = pNode->next;
if (!pNode || !_ppNode) {
printf("鏈表爲空!\n");
return;
}
printf("打印鏈表:\n");
while (_ppNode) {
printf("%d\t",_ppNode->data);
_ppNode = _ppNode->next;
}
printf("\n打印結束\n");
}
鏈表逆序(這個應該用前插法,我這寫法太笨了,大家就不要看了。)
void ReverseLinkList(PNode *ppNode)
{
if (!ppNode || !(*ppNode)) {
printf("鏈表爲空!\n");
return;
}
PNode left_pNode = NULL;//反轉鏈表用
PNode right_pNode = NULL;
PNode _ppNode = (*ppNode)->next;
while (_ppNode) {
right_pNode = _ppNode->next;
_ppNode->next = left_pNode;
left_pNode = _ppNode;
_ppNode = right_pNode;
}
(*ppNode)->next = left_pNode;
}
void UnionLinkList(PNode *ppNode, PNode pNode)
{
if (!ppNode || !(*ppNode) || !pNode) {
printf("鏈表非法!\n");
return;
}
PNode tmp_pNode = pNode->next;//保存從pNode中取出的節點
PNode pri_pNode = (*ppNode);//保存當前遍歷節點的前一個節點
PNode cur_pNode = pri_pNode->next;//保存當前節點
pNode = pNode->next;
while (tmp_pNode && cur_pNode)
{
while (cur_pNode)
{
if (tmp_pNode->data <= cur_pNode->data)
{
pNode = pNode->next;
tmp_pNode->next = cur_pNode;
pri_pNode->next = tmp_pNode;
tmp_pNode = pNode;
break;
}
else
{
pri_pNode = cur_pNode;
cur_pNode = cur_pNode->next;
}
}
}
if (tmp_pNode && !cur_pNode) {
pri_pNode->next = tmp_pNode;
}
}
判斷鏈表是否存在環
原理:通過快慢指針實現。設慢指針slow_pNode(一次走一步),快指針fast_pNode(一次走兩步)。 假設當慢指針剛進入換的時候,快指針已近在環中走過了m步。則,當慢指針在環中走過 i 步時,快指針走過了m+2 * i步,此時快指針比慢指針多走了step = m + 2*i - i = m + i步。一定存在一個 i 值,使得step = n(環的長度)。代碼如下:
Status IsCircleLinkList(PNode pNode, PNode *mtpNode)
{
if (!pNode){
printf("鏈表爲空!\n");
return ERROR;
}
PNode slow_pNode = pNode;
PNode fast_pNode = pNode;
while (fast_pNode)
{
if (!fast_pNode->next) {
return OK;
}
fast_pNode = fast_pNode->next->next;
slow_pNode = slow_pNode->next;
if (fast_pNode == slow_pNode)//有環
{
(*mtpNode) = fast_pNode;//把相遇節點傳出來,便於尋找入口節點。
return ERROR;
}
else if (!fast_pNode)
{
return OK;
}
}
return OK;
}
尋找入口節點:
原理:以相遇節點爲標識,在邏輯上將有環鏈表分成兩個單鏈表pNode 和 _pNode。然後計算兩個鏈表長度的差step,讓較長鏈表先走step步,之後兩個鏈表挨個節點進行比較,直至找到相同節點,即爲入口節點(該原理類似從兩個不同的單鏈表中找出其相同的節點)。
圖例如下:(最後的節點9不是指向NULL,而是指向前面的節點5)
1-->2-->3-->4--5-->6-->7-->8-->9
^ |
|____________|
此時,假設相遇節點爲7,以7爲標示,將該鏈表從邏輯上分爲以下兩個鏈表:
1-->2-->3-->4--5>--6>-->7
7-->8-->9-->5-->6
實現代碼:(pNode相當於上面的1節點地址,_pNode相當於7節點地址)
PNode FindEntryNode(PNode pNode, PNode _pNode)
{
int len1 = 0, len2 = 0;
PNode tmp_pNode = pNode->next;
while (tmp_pNode != _pNode) {
++len1;
tmp_pNode = tmp_pNode->next;
}
len1 += 1;
tmp_pNode = _pNode->next;
while (tmp_pNode != _pNode) {
++len2;
tmp_pNode = tmp_pNode->next;
}
len2 += 1;
int step = abs(len1 - len2);
while (step) {
pNode = pNode->next->next;
step--;
}
tmp_pNode = _pNode;//由環解開的鏈表
while (pNode != tmp_pNode) {//沒走到虛擬的鏈表尾部,實際就是從相遇節點把鏈表分成兩個假象的單鏈表
if(pNode == _pNode){
break;
}
pNode = pNode->next;
_pNode = _pNode->next;
}
return pNode;
}
//靜態鏈表
實現原理如圖:
初始化鏈表
Status InitStaticLinkList(StaticLinkList space)
{
int i;
for (i = 0; i < MAXSIZE - 1; i++) {
space[i].cur = i+1;
}
space[MAXSIZE -1].cur = 0;
return OK;
}
int Malloc(StaticLinkList space)
{
if (space == NULL) {
return 0;
}
int i = space[0].cur;
if (i) {
space[0].cur = space[i].cur;
}
return i;
}
釋放節點
void Free(StaticLinkList space, int j)
{
space[j].cur = space[0].cur;
space[0].cur = j;
}
返回靜態鏈表長度
int StaticLinkListLength(StaticLinkList L)
{
int i = L[MAXSIZE -1].cur;
int length = 0;
while (i) {
++length;
i = L[i].cur;
}
return length;
}
Status StaticListInsert(StaticLinkList L, int i, ElemType e)
{
int cur = Malloc(L);
int k = MAXSIZE -1;
if (L == NULL || i < 1 || i > StaticLinkListLength(L)+1) {
return ERROR;
}
if (cur) {
L[cur].data = e;
for (int m = 0; m < i - 1; m++) {
k = L[k].cur;
}
L[cur].cur = L[k].cur;
L[k].cur = cur;
return OK;
}
return ERROR;
}
Status StaticLinkListDelete(StaticLinkList L, int i)
{
if (L == NULL || i < 1 || i > StaticLinkListLength(L)) {
return ERROR;
}
int k = MAXSIZE - 1;
for (int m = 0; m < i - 1; m++) {
k = L[k].cur;
}
int j = L[k].cur;
L[k].cur = L[j].cur;
Free(L, j);
return OK;
}