单链表

来源:互联网 发布:中羽论坛推荐淘宝店 编辑:程序博客网 时间:2024/06/10 13:16


链表Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存放到下一个节点的指针(Pointer)。使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

链表有很多种不同的类型:单向链表,双向链表以及循环链表

本章主要讲解单向链表

1、插入

下面是链表的按序插入过程(以结点中key值的大小作为插入依据,递增排序)。

其实现的过程考虑三种情况(1、2可以合并为一种情况):

1)当L为空;

2)在链表的头部插入;

3)在链表的中间插入(包括在链表尾部插入的情况)。

 

//=============================================================// 按增序插入结点//=============================================================LNode *ListInsert1(LIST l, LNode *x){if (l == NULL)         // case 1return x;if (x->key < l->key) { // case 2// 在l结点前插入x->pNext = l;l = x;} else {               // case 3// 获得x的前驱LNode *pPrev = l;   while (pPrev->pNext && pPrev->pNext->key < x->key) {pPrev = pPrev->pNext;}// 在perv结点后插入x->pNext = pPrev->pNext;pPrev->pNext = x;}return l;}//=============================================================// 按增序插入结点//=============================================================LNode *ListInsert2(LIST l, LNode *pCur){// case 1与case 2可以合并为一种情况,头部插入if (l == NULL || pCur->key < l->key) {// 在l结点前插入pCur->pNext = l;l = pCur;} else {               // case 3:中间插入(包括尾部插入)// 获得pCur的前驱 LNode *pPrev = l;while (pPrev->pNext && pPrev->pNext->key < pCur->key) {pPrev = pPrev->pNext;}// 在pPrev结点后插入pCur->pNext = pPrev->pNext;pPrev->pNext = pCur;}return l;}

使用二级指针进行链表的插入操作,将各种情况归一化,可以简化代码,但理解起来有难度。


//=============================================================// 按增序插入结点//============================================================= void ListInsert3(LNode **ppList, LNode *pCur){LNode **ppIter = ppList;if (ppIter == NULL || pCur == NULL) {return;}while ((*ppIter) != NULL && (*ppIter)->key < pCur->key) {ppIter = &((*ppIter)->pNext);}pCur->pNext = (*ppIter); (*ppIter) = pCur;}void ListInsert4(LNode **ppList, LNode *pCur){LNode **ppIter = ppList;if (ppIter == NULL ||  pCur == NULL) {return;}while ((*ppIter) != NULL) {if (pCur->key < (*ppIter)->key) {pCur->pNext = (*ppIter);break;} ppIter = &((*ppIter)->pNext);}(*ppIter) = pCur;}//=============================================================// 在ppList尾部插入//=============================================================void ListInsert5(LNode **ppList, LNode *pCur){LNode **ppIter = ppList;if (ppIter == NULL || pCur == NULL) {return;}while ((*ppIter) != NULL) {ppIter = &((*ppIter)->pNext);}(*ppIter) = pCur;}

2、删除

删除和插入是一个相反的过程,一般需要先找到删除结点的前驱(prev

1)删除链表的首结点

2)删除链表的中间结点(包括删除链表尾结点的情况)

 

//=============================================================// 删除结点,根据key值//=============================================================LIST ListDelete1(LIST l, int key){LNode *pCur = NULL;if (l == NULL)return NULL;if (l->key == key) { // case 1:删除首结点pCur = l;l = l->pNext;free(pCur);} else {             // case 2:删除中间结点(包括尾结点)// 获得key的前驱LNode *pPrev = l;while (pPrev->pNext && pPrev->pNext->key != key) { pPrev = pPrev->pNext;}// 如果找到,删除key结点if (pPrev->pNext != NULL) {pCur = pPrev->pNext;pPrev->pNext = pCur->pNext;free(pCur);}}return l;}
使用二级指针进行链表的删除操作,将各种情况归一化,可以简化代码,但理解起来有难度。


//=============================================================// 删除结点,根据key值//=============================================================void ListDelete2(LNode **ppList, int key){LNode **ppIter = ppList;if (ppIter == NULL && (*ppIter) == NULL) {return;}// 获得key的前驱(包含首结点、中间结点、尾结点三种情况)while ((*ppIter) != NULL && (*ppIter)->key !=  key) {ppIter = &((*ppIter)->pNext);}// 如果找到,删除key结点if ((*ppIter) != NULL) {LNode *pTmp = (*ppIter);(*ppIter) = (*ppIter)->pNext;free(pTmp);}}//=============================================================// 删除尾结点//=============================================================void ListDelete3(LNode **ppList){LNode **ppIter = ppList;if (ppIter == NULL && (*ppIter) == NULL) {return;}while (((*ppIter)->pNext) != NULL) {ppIter = &((*ppIter)->pNext);}free(*ppIter);(*ppIter) = NULL;}

总结:使用二级指针进行链表的插入(删除)操作,(*ppIter)相当于充当了一下哑结点,将首部的插入(删除)操作归一。

3、反转

对单链表进行反转操作,图(2)、(3)为反转的过程。

 

//=============================================================// 对指定的链表进行反转//=============================================================LIST ListReverse(LIST l){LNode *pCur = l; // 当前结点LNode *pPrev = NULL; LNode *pNext = NULL; while (pCur) {pNext = pCur->pNext;pCur->pNext = pPrev; // 反转pPrev = pCur;        // pPrev和pCur往前移动。pCur = pNext;}return pPrev;}

4、合并

将两个已排序的链表进行合并

 

//=============================================================// 合并两个链表(増序)//=============================================================LIST ListMerge(LIST l1, LIST l2){LNode *l = NULL;LNode **pos = &l;while (l1 && l2) {if (l1->key < l2->key) {*pos = l1;l1 = l1->pNext;} else {*pos = l2;l2 = l2->pNext;}pos = &((*pos)->pNext); // 注意,pos为指向下一个结点指针的地址}if (l1) { *pos = l1;} else {*pos = l2;}return l;}

/******************************************************** * 简单的单链表(无哑结点)  *******************************************************/#include <stdio.h>#include <stdlib.h>typedef struct lnode {int key;struct lnode *next;} LNode, *LIST;//=============================================================// 根据key值,获得该节点位置//=============================================================static inline LNode *ListSearch(LIST l, int key){LNode *p = l;while (p && p->key != key)p = p->next;return p;}//=============================================================// 获取x结点的前驱//=============================================================static inline LNode *GetPrev(LIST l, LNode *x){LNode *prev = l;while (prev && prev->next != x)prev = prev->next;return prev;}//=============================================================// 按序插入结点//=============================================================LNode *ListInsert(LIST l, LNode *x){if (l == NULL) // case 1return x;if (x->key < l->key) { // case 2x->next = l;l = x;} else {               // case 3// 获得x的前驱LNode *prev = l;   while (prev->next && prev->next->key <= x->key)prev = prev->next;x->next = prev->next;prev->next = x;}return l;}
//=============================================================// 对指定的链表进行反转//=============================================================LIST ListReverse(LIST l){LNode *cur = l; // 当前结点LNode *prev = NULL; LNode *next = NULL; while (cur){next = cur->next;cur->next = prev; // 反转prev = cur; // prve和cur往前移动。cur = next;}return prev;}//=============================================================// 合并两个链表(増序)//=============================================================LIST ListMerge(LIST l1, LIST l2){LNode *l = NULL;LNode **pos = &l;while (l1 && l2) {if (l1->key < l2->key) {*pos = l1;l1 = l1->next;} else {*pos = l2;l2 = l2->next;}pos = &(*pos)->next; // 注意,pos指向下一个结点的地址} if (l1)  *pos = l1;else*pos = l2;return l;}//=============================================================// 交换链表中两结点的位置//=============================================================LIST ListSwap(LIST l, LNode *x, LNode *y){if (l == NULL)return l;// 获取x、y的前驱LNode *xprev = GetPrev(l, x);LNode *yprev = GetPrev(l, y);LNode *xnext = x->next; // 记录x->next = y->next; // 相当于插入xif (yprev == NULL) // y为头结点l = x;elseyprev->next = x;y->next = xnext;   // 相当于插入yif (xprev == NULL) // x为头结点l = y;elsexprev->next = y;// if (xprev == NULL) {// LNode *xnext = x->next;// x->next = y->next; // 相当于插入x// yprev->next = x;// y->next = xnext;   // 相当于插入y// l = y;// } else if (yprev == NULL){// LNode *ynext = y->next;// y->next =x->next;// xprev->next = y;// x->next = ynext;// l = x;// } else {// LNode *xnext = x->next;// x->next = y->next; // 相当于插入x// yprev->next = x;// y->next = xnext;   // 相当于插入y// xprev->next = y;// }return l;}//=============================================================// 对指定的链表进行输出//=============================================================void ListPrint(LIST l){LNode *p = l;while (p) {printf("%d\t", p->key);p = p->next;}printf("\n");}int main(){int arr[] = {34, 2, 53, 21, 354, 78};int len = sizeof(arr) / sizeof(arr[0]);LIST l1 = NULL;LIST l2 = NULL;LIST l = NULL;printf("Insert:\n");for (int i = 0; i < len; i++) {LNode *x = (LNode *)malloc(sizeof(LNode));if (!x) {printf("x malloc error\n");return -1;}x->key = arr[i];x->next = NULL;l1 = ListInsert(l1, x);ListPrint(l1);LNode *y = GetPrev(l1, ListSearch(l1, arr[i]));if (y == NULL)printf("%d is not prev!\n", arr[i]);elseprintf("%d prev is %d\n", arr[i], y->key);}printf("Insert:\n");for (int i = 0; i < len - 2; i++) {LNode *x = (LNode *)malloc(sizeof(LNode));if (!x) {printf("x malloc error\n");return -1;}x->key = arr[i] * 2 - 40;x->next = NULL;l2 = ListInsert(l2, x);ListPrint(l2);}printf("Merge:\n");l = ListMerge(l1, l2);ListPrint(l);printf("Delete:\n");l = ListDelete(l, ListSearch(l, -36));ListPrint(l);printf("Reverse:\n"); l = ListReverse(l); ListPrint(l);printf("Swap():\n");l = ListSwap(l, ListSearch(l, arr[1]), ListSearch(l, arr[4]));ListPrint(l);// printf("Delete:\n");// for (int i = 0; i < len; i++) {// l = ListDelete(l, ListSearch(l, arr[i]));// ListPrint(l);// }LNode *y = GetPrev(l, ListSearch(l, 53));printf("53 prev is %d\n", y->key);system("pause");return 0;}


0 0