常见的链表操作
-
链表的操作主要体现在对指针的操作,考察对指针的理解。
- 对链表的操作关键在于对链表的遍历,一种是常规正序遍历,另一种是逆序时采用的递归遍历。
- 链表复杂操作可能会需要多个指针协调工作。
- 注意考虑链表为空的情况。
//链表定义 typedef struct LNode{ int data; struct LNode *next; }LNode,*LinkList; typedef struct LNode_C{ char data; struct LNode_C *next; }LNode_C,*LinkList_C; typedef struct LNode_Dul{ int data; struct LNode_Dul *next; struct LNode_Dul *prior; int freq; }LNode_Dul,*LinkList_Dul;
一、链表的创建
顺序生成一个有头结点单链表,元素类型为int
//顺序生成一个有头结点单链表,元素类型为int void CreatList(LinkList *ptr_L) { *ptr_L = new LNode(); (*ptr_L)->next = NULL; LinkList p = *ptr_L; int data; while (cin>>data) { LinkList temp_L = new LNode(); temp_L->next = NULL; temp_L->data = data; p->next = temp_L; p = temp_L; } }
逆序生成一个有头结点单链表,元素类型为int
//逆序生成一个有头结点单链表,元素类型为int void CreatList_L(LinkList &L,int n){ L=(LinkList)malloc(sizeof(LNode)); L->next=NULL; cout<<"请从后到前输入链表内容:"<<endl; for (int i=n;i>0;i--) { LinkList p=(LinkList)malloc(sizeof(LNode)); cin>>p->data; p->next=L->next; L->next=p; } }
逆序生成一个有头结点单循环链表,元素类型为int
//逆序生成一个有头结点单循环链表,元素类型为int void CreatList_L_Cir(LinkList &L,int n){ L=(LinkList)malloc(sizeof(LNode)); L->next=L;//最后元素指向L cout<<"请从后到前输入链表内容:"<<endl; for (int i=n;i>0;i--) { LinkList p=(LinkList)malloc(sizeof(LNode)); cin>>p->data; p->next=L->next; L->next=p; } }
逆序生成一个有头结点双循环链表,元素类型为int
//逆序生成一个有头结点双循环链表,元素类型为int void CreatList_L_Cir_Dul(LinkList_Dul &L,int n){ L=(LinkList_Dul)malloc(sizeof(LNode_Dul)); L->next=L;//最后元素指向L cout<<"请从后到前输入链表内容:"<<endl; for (int i=n;i>0;i--) { LinkList_Dul p=(LinkList_Dul)malloc(sizeof(LNode_Dul)); if(i==n) L->prior=p; p->freq=0; cin>>p->data; p->prior=L;//前驱 L->next->prior=p;//前驱 p->next=L->next;//后继 L->next=p;//后继,此处L->暂存p以供下次循环使用 } }
二、单链表的操作
链表反转
//链表反转。先遍历,然后前方插入 void InverseList(LinkList *L) { if ((*L) == NULL) { throw "error"; } if ((*L)->next == NULL || (*L)->next->next ==NULL) { return; } LinkList pre = (*L)->next; LinkList p = (*L)->next->next; LinkList tmpFirst = (*L)->next; while(p) { LinkList tmp = p->next; (*L)->next = p; p->next = pre; pre = p; p = tmp; } tmpFirst->next = NULL; }
在带头结点的单链表结构上实现线性表操作,定位值为x的结点
//在带头结点的单链表结构上实现线性表操作,定位值为x的结点 int Locate(LinkList L,int x){ int i=0; LinkList p=L; while(p&&p->data!=x){ p=p->next; i++; } if (!p) return 0; return i; }
在带头结点的单链表结构上实现线性表操作,求链表长度
//在带头结点的单链表结构上实现线性表操作,求链表长度 int ListLength_L(LinkList L){ if (L == NULL) { throw "error"; } int i=0; LinkList p=L->next; while (p){ p=p->next; i++; } return i; }
查找倒数第K个节点。
//查找倒数第K个节点。设立双指针,先让第一个遍历到第k个节点,与此同时,第二个指针开始从头遍历,当第一个节点到尾部时,第二个节点即为倒数第k个节点 LNode* FindNodeReverse(LinkList L, int k) { if (L == NULL || k == 0) { throw "error"; } LinkList p = L->next; LinkList q = L->next; int i = 0; while(p && i < k) { i++; p = p->next; } if (i < k) { return NULL; } else { while(p) { p = p->next; q = q->next; } return q; } }
查找单链表中间元素,即第(n/2+1)个结点。
//查找单链表中间元素,即第(n/2+1)个结点。 LNode* FindMiddleNode(LinkList L) { if (L == NULL) { throw "error"; } if (L->next == NULL) { return NULL; } LinkList p1 = L; LinkList p2 = L; while(p1 && p2) { p1 = p1->next; p2 = p2->next; if (p2) { p2 = p2->next; } } return p1; }
从尾到头打印单链表,使用栈
// 从尾到头打印单链表,使用栈 void VisitListReverse_Stack(LinkList L) { if (L == NULL) { throw "error"; } stack<LNode> s; LinkList p = L; while(p) { s.push(*p); p = p->next; } while(!s.empty()) { cout<<((s.top()).data)<<' '; s.pop(); } }
从尾到头打印单链表,递归
// 从尾到头打印单链表,递归 void VisitListReverse_Recur(LinkList L) { if (L == NULL) { return; } else { VisitListReverse_Recur(L->next); cout<<L->data<<' '; } }
判断一个单链表中是否有环。
//判断一个单链表中是否有环。设立快慢指针,快指针一定在环内追赶上慢指针。 bool HasCircle(LinkList L) { if (L == NULL) { throw "error"; } LinkList p1 = L; LinkList p2 = L; while(p2->next != NULL && p2->next->next != NULL) { p1 = p1->next; p2 = p2->next->next; if (p1 == p2) { return true; } } return false; }
已知一个单链表中存在环,求进入环中的第一个节点。
//已知一个单链表中存在环,求进入环中的第一个节点。先判断是否存在环,找出相遇节点,然后将原链表转换为两个相交的链表,一个为从原头结点到相遇节点,另一个为从相遇节点的下一个节点到相遇节点 LNode* FindFirstCircleNode(LinkList L) { if (L == NULL) { throw "error"; } LinkList p1 = L; LinkList p2 = L; while(p2->next != NULL && p2->next->next != NULL) { p1 = p1->next; p2 = p2->next->next; //先判断是否存在环 if (p1 == p2) { LinkList ptr_circle = p1->next;//新链表1的头结点 int len_circle = 1; while(ptr_circle != p1) { ptr_circle = ptr_circle->next; len_circle++; } LinkList ptr_in = L;//新链表2的头结点 int len_in = 1; while(ptr_in != p1) { ptr_in = ptr_in->next; len_in++; } //求取1、2两个链表的长度差值 if (len_circle > len_in) { int k = len_circle - len_in; while (k--) { ptr_circle = ptr_circle->next; } } else { int k =len_in - len_circle; while (k--) { ptr_in = ptr_in->next; } } //求取第一个相遇节点 while(ptr_circle != ptr_in) { ptr_circle = ptr_circle->next; ptr_in = ptr_in->next; } return ptr_circle; } } return NULL; }
给出一单链表头指针pHead和一节点指针pToBeDeleted,O(1)时间复杂度删除节点pToBeDeleted。
//给出一单链表头指针pHead和一节点指针pToBeDeleted,O(1)时间复杂度删除节点pToBeDeleted。将待删除节点的下一个节点的内容复制到待删除节点,然后删除待删除节点下一节点 void Delete(LinkList pHead, LNode* pToBeDeleted) { if (pHead == NULL || pToBeDeleted == NULL || pHead == pToBeDeleted) { throw "error"; } LinkList tmp = pToBeDeleted->next; if (tmp != NULL) { pToBeDeleted->data = pToBeDeleted->next->data; pToBeDeleted->next = pToBeDeleted->next->next; delete tmp; } else//待删除节点为最后一个节点 { tmp = pHead; while(tmp->next != NULL && tmp->next->next != NULL) { tmp = tmp->next; } tmp->next = NULL; delete pToBeDeleted; } }
删除表中所有值大于mink且小于maxk的元素
//已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试写一高效的算法,删除表中所有值大于mink且小于maxk的元素(若表中存在这样的元素),同时释放被删结点空间,并分析你的算法的时间复杂度 int ListDelete_L(LinkList &L,float mink, float maxk){ if (mink>maxk) return -2; LinkList p=L->next, p_pre=L, q; while(p && p->data<maxk){ if(p->data<=mink){ p_pre=p; p=p->next; } else{ q=p; p=p->next; free(q); } } p_pre->next=p; return 0; }
删除表中所有值相同多余元素
//已知线性表中的元素以值递增有序排列,并以单链表作存储结构。试写一高效的算法删除表中所有值相同多余元素。 void ListDelete_LSameNode(LinkList &L){ LinkList p=L, p_pre=L, q; p=p->next; while(p->next){ p_pre=p; p=p->next; while(p && p_pre->data==p->data){//可能有多个和pre相同的元素,递增p p_pre->next=p->next; q=p; p=p->next; free(q); } if (!p) break; } }
带freq域的双向循环链表上的查找
//带freq域的双向循环链表上的查找 LNode_Dul * Locate_DuList(LinkList_Dul &L,int x){ LinkList_Dul p=L->next, q=NULL; while(p!=L && p->data!=x){ p=p->next; } if(p==L) return NULL; ++(p->freq); q=p->prior; while(q!=L && q->freq<p->freq){ q=q->prior; } //删除p p->prior->next=p->next; p->next->prior=p->prior; //插入p p->next=q->next;//先将用到q->next指针处理完 q->next->prior=p; q->next=p;//然后改变q->next值 p->prior=q; return p; }
三、多链表操作
判断两个单链表是否相交
// 判断两个单链表是否相交 bool Intersect(LinkList L1, LinkList L2) { if (L1 == NULL || L2 == NULL) { throw "error"; } LinkList p1 = L1; while(p1->next != NULL) { p1 = p1->next; } LinkList p2 = L2; while(p2->next != NULL) { p2 = p2->next; } if (p1 == p2) { return true; } return false; }
求两个单链表相交的第一个节点
// 求两个单链表相交的第一个节点。先对齐两个链表的当前结点,使之到尾节点的距离相等 LNode* FindFirsrtIntersect(LinkList L1, LinkList L2) { if (L1 == NULL || L2 == NULL) { throw "error"; } LinkList p1 = L1; int len1 = 0; while(p1->next != NULL) { len1++; p1 = p1->next; } int len2 = 0; LinkList p2 = L2; while(p2->next != NULL) { len2++; p2 = p2->next; } //如果相交,则求相交节点 if (p1 == p2) { if (len1 < len2) { int i = len2 - len1; p2 = L2; while(i--) { p2 = p2->next; } } else { int i = len1 - len2; p1 = L1; while(i--) { p1 = p1->next; } } while(p1 != p2) { p1 = p1->next; p2 = p2->next; } return p1; } return NULL; }
已知两个单链表a 和b 各自有序,把它们合并成一个链表依然有序
// 已知两个单链表a 和b 各自有序,把它们合并成一个链表依然有序 void MergeSortedList(LinkList a,LinkList b,LinkList &c) { if (a == NULL && b == NULL) { throw "error"; return; } else if (b != NULL && a == NULL) { c = b; return; } else if (a != NULL && b == NULL) { c = a; return; } LinkList p1 = a; LinkList p2 = b; LinkList p = NULL; if (p1->data != NULL && p2->data != NULL && p1->data > p2->data) { p = c = p2; } else { p = c = p1; } p1 = p1->next; p2 = p2->next; while(p1 && p2) { if (p1->data > p2->data) { p->next = p2; p = p2; p2 = p2->next; } else { p->next = p1; p = p1; p1 = p1->next; } } //连接未遍历的后续节点 if (p1) { p->next = p1; } else { p->next = p2; } }
已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序,递归
// 已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序,递归 LNode* MergeSortedList_Recursive(LinkList pHead1,LinkList pHead2) { if(pHead1 == NULL) return pHead2; if(pHead2 == NULL) return pHead1; LinkList pHeadMerged = NULL; if(pHead1->data < pHead2->data) { pHeadMerged = pHead1; pHeadMerged->next = MergeSortedList_Recursive(pHead1->next, pHead2); } else { pHeadMerged = pHead2; pHeadMerged->next = MergeSortedList_Recursive(pHead1, pHead2->next); } return pHeadMerged; }
假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结构,请编写算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许表中含有值相同的元素)排列的线性表C,并要求利用原表(即A表和B表)的结点空间构造C表。
//假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结构,请编写算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许表中含有值相同的元素)排列的线性表C,并要求利用原表(即A表和B表)的结点空间构造C表。 int ListMergeOppose_L(LinkList &A,LinkList &B,LinkList &C){ LinkList pa=A->next, pb=B->next, pc_pre=NULL, pc, q; while(pa || pb){ if (pa && (!pb || pa->data <= pb->data)){//注意if的条件 pc=pa; q=pa->next; pa->next=pc_pre; pa=q; } else{ pc=pb; q=pb->next; pb->next=pc_pre; pb=q; } pc_pre=pc; } C=A;A->next=pc; return 0; }
求元素递增排列的线性表A和B的元素的交集并存入C中
//求元素递增排列的线性表A和B的元素的交集并存入C中 void LinkList_Intersect(LinkList A,LinkList B,LinkList &C){ LinkList pa=A->next, pb=B->next, pc; C=pc=(LNode*)malloc(sizeof(LNode)); while(pa && pb){ if (pa->data<pb->data){ pa=pa->next; } else if(pa->data<pb->data){ pb=pb->next; } else{ LinkList pc1=(LNode*)malloc(sizeof(LNode)); pc->next=pc1; pc1->data=pa->data; pc1->next=NULL; pc=pc1; pa=pa->next; pb=pb->next; } } }
把单链表L的元素(char)按类型分为三个循环链表。
//把单链表L的元素(char)按类型分为三个循环链表。A, B, C为带头结点的单循环链表类型。 int LinkList_Divide(LinkList_C &L, LinkList_C &A, LinkList_C &B, LinkList_C &C){ LinkList_C s=L->next, a, b, c; A=a=(LinkList_C)malloc(sizeof(LNode_C)); B=b=(LinkList_C)malloc(sizeof(LNode_C)); C=c=(LinkList_C)malloc(sizeof(LNode_C)); while(s){ if (isalpha(s->data)) { a->next=s; a=a->next; } else if(isdigit(s->data)){ b->next=s; b=b->next; } else{ c->next=s; c=c->next; } s=s->next; } a->next=A; b->next=B; c->next=C; return 0; }
所有函数都经过验证,如果大家发现有错误,欢迎留言,谢谢。