链表属于线性表的范畴。线性表有顺序的存储结构实现,代码如下:
typedef int ElemType; typedef struct{ Elem elem[maxsize]; int length; }SqList;
同时线性表还有链式存储结构,包括单链表,双链表,循环链表,静态链表。
单链表:只能向前遍历,最后一个节点为NULL,判断结束用while(p)。编程的时候只要处理next指针就行了。
双链表:可以两个方向遍历,双链表的尾节点指针为NULL。在编程的时候注意要处理next和prev指针,其他和单链表一样。
循环链表:只能向前遍历,但是最后一个指向头结点。这样判断结束的时候用while(p!=head)。编程的时候注意最后一个节点的处理。
静态链表:借用一维数组来描述线性链表。数组中的一个分量表示一个节点,同时使用游标代替指针指示下一个节点在数组中的位置,第0个节点可以不存数,表示Head节点。这种存储结构需要首先分配很大的空间,但是在进行插入和删除的时候不需要移动元素,仅需要修改游标就行了。
typedef char ElemType[10] typedef struct{ ElemType data; int next; }StaticList[MaxSize];
本文只简单的写了链表的操作。
链表的实现
好久没用C语言写代码了,手生了,得找点感觉,不然面试就悲剧了。本文尽量采用C语言的风格,可是写出的代码还是夹杂着C++风格,关键我用的visual studio。简单的总结一下,后面复习的时候用到。链表实现的时候主要注意一下几点:
(1)链表编代码的时候不要忘了最后一个节点加上“NULL”,C++里面有构造函数,构造出来next就是NULL,所以出现这个问题比较少。
(2)链表的插入和删除操作,需要扫一遍链表,先判定是否存在该节点。
(3)链表的创建有头插法和尾插法两种。头插法创造出来的链表是反的,尾插法就是顺序的。
(4)不要忘了释放链表。(本文没有编写释放)
(5)在子函数里面分配节点有如下两种方法:
//方法1 int *test() { int *t = (int*)malloc(sizeof(int)); return t; } //方法2 void test(int **t) { *t = (int*)malloc(sizeof(int)); } test(&pHead);//调用
链表实现代码:
这边只实现了几个简单操作,其他应该也不难。我这边实现的代码都有一个额外的头结点。
#include<cstdio> #include<cstdlib> #include<iostream> using namespace std; typedef int ElemType; typedef struct ListNode{ ElemType data; struct ListNode* next; }LinkList; void InitNode(LinkList **head) { *head = (LinkList*)malloc(sizeof(LinkList)); (*head)->next = NULL; } void CreateList(LinkList* pHead,ElemType *arr,int len) { LinkList *pCurrent = pHead; for (int i=0;i<len;++i) { LinkList *newNode = (LinkList*)malloc(sizeof(LinkList)); if(!newNode){ --i; continue; } newNode->data = arr[i]; pCurrent->next = newNode; pCurrent = newNode; } pCurrent->next = NULL;//别忘掉 } void DisplayList(LinkList* pHead) { if(!pHead)return; for(pHead=pHead->next;pHead;pHead=pHead->next) printf("%d ",pHead->data); }
链表的常用操作
(1)链表的排序
方法1:选择排序
这里我简单借助了选择排序,我并没有直接对节点进行重组,而只是去交换节点的值,代码测试没有问题。
void SortList(LinkList* pHead) { LinkList *minPtr; for(LinkList* ptrA=pHead->next;ptrA&&ptrA->next;ptrA =ptrA->next){ minPtr = ptrA; for(LinkList *ptrB =ptrA->next;ptrB;ptrB = ptrB->next){ if(ptrB->data<minPtr->data) minPtr = ptrB; } if(minPtr!=ptrA) { ElemType temp = ptrA->data; ptrA->data = minPtr->data; minPtr->data =temp; } } }
方法2:(插入排序)
void InsertSortList(LinkList* pHead) { if (!pHead->next)return; LinkList *p,*q = pHead->next->next; pHead->next->next = NULL;//将第一节点断开 while(q){ LinkList *tmp = q->next; //p->next,p节点不为NULL for(p = pHead;p->next&&p->next->data<q->data;p=p->next); q->next = p->next; p->next = q; q = tmp; } }
(2)链表的反序操作
画个图简单的说明下。
void ReverseList(LinkList *pHead) { if(NULL == pHead->next)return; LinkList *ptr = pHead->next->next; LinkList *pCurrent = pHead->next; pCurrent->next = NULL;//一定要 while(ptr){ LinkList *tmp = ptr->next; ptr->next = pCurrent; pHead->next = ptr; pCurrent = ptr; ptr = tmp; } }
贴一个递归的代码,感觉写的不是很好,有没有更好的:
LinkList* ReverseListRecursively(LinkList *pHead,LinkList **pReverseHead) { if(pHead==NULL) return NULL; if(pHead->next == NULL) { *pReverseHead = pHead; return pHead; } LinkList *pRet = ReverseListRecursively(pHead->next,pReverseHead); pRet->next = pHead; pHead->next = NULL; }
呵呵:这哥们代码不错:点击打开链接
void MergeList(LinkList* pHead1,LinkList*pHead2,LinkList* pHead) { LinkList *tmp = pHead1; pHead1 = pHead1->next; free(tmp); tmp = pHead2; pHead2 = pHead2->next; free(tmp); while(pHead1&&pHead2){ if(pHead1->data<pHead2->data){ pHead->next = pHead1; pHead = pHead->next; pHead1 = pHead1->next; } elseif(pHead1->data>pHead2->data){ pHead->next = pHead2; pHead = pHead->next; pHead2 = pHead2->next; } else{ pHead->next = pHead1; pHead1 = pHead1->next; tmp = pHead2; pHead2 = pHead2->next; free(tmp); pHead = pHead->next; } } if(pHead1)pHead->next = pHead1; if(pHead2)pHead->next = pHead2; }
void CommmonNode(LinkList* hA,LinkList* hB,LinkList *hC) { LinkList *pA = hA->next,*pB = hB->next,*pC = hC->next; hA->next = NULL; LinkList *pCur = hA; while(pA){ while(pB&&pB->data<pA->data)pB = pB->next; while(pC&&pC->data<pA->data)pC = pC->next; if(pC->data==pA->data&&pB->data==pA->data){ pCur->next = pA; pCur = pA; pA = pA->next; } else{ LinkList *tmp = pA; pA = pA->next; free(tmp); } pCur->next = NULL; } }
(5)求出链表中倒数第K个元素
解法:很清晰就是用两个指针,第一个指针先跑到第K个元素,然后两个指针一起跑。但是面试写代码要注意考虑表的长度不够或者空表的现象。
ElemType TheLastKthNode(LinkList *pHead,int k) { if(!pHead&&!pHead->next){ cerr<<"Empty list"<<endl; return -1; } LinkList *pFirst = pHead->next; LinkList *pSecond = pHead->next; int i; for(i=0;pFirst&&i<k;i++,pFirst=pFirst->next); if(i!=k){ cerr<<"No enough nodes"<<endl; return -1; } for(;pFirst;pFirst=pFirst->next,pSecond=pSecond->next); return pSecond->data; }
(6)求两个链表的交点问题:用长度的方法或者判断链表是否带圈。(不给出代码)
(7)链表没有头结点,但是要删除一个节点。(编程之美狸猫换太子)
(8)测试代码
int main() { LinkList *pHead1,*pHead2,*pHead; InitNode(&pHead1); InitNode(&pHead2); InitNode(&pHead); ElemTypearr1[]={3,2,5,4}; ElemType arr2[]={3,5,2,8}; CreateList(pHead1,arr1,4); CreateList(pHead2,arr2,4); SortList(pHead1); SortList(pHead2); DisplayList(pHead1); cout<<endl;//c++ DisplayList(pHead2); cout<<endl;//c++ MergeList(pHead1,pHead2,pHead); DisplayList(pHead); system("pause"); return 0; }
希望后面还有链表算法的时候,还能增加在这里。
后记
新手,勿笑。