问题描述:
常见的单链表操作题目:
- 1、判断单链表是否有环,若有环求环的入口点
- 2、求单链表倒数第m个节点
- 3、两个单链表是否相交,若相交求交点
- 4、单链表原地逆序
单链表List定义如下:
#ifndef List_H #define List_H #include <iostream> using namespace std; struct node { int data; node* next; }; class List { private: node* head; public: List() { head=new node(); head->data=-1; head->next=NULL; } void push_back(int v) { node* pnode=new node(); pnode->data=v; pnode->next=head->next; head->next=pnode; } node* first() { return head->next; } node* last() { node* p=head->next; while (p->next!=NULL) { p=p->next; } return p; } //返回单链表第m个节点,单链表长度大于m node* getPtr(int m) { int i=1; node* p=first(); while (i<m) { i++; p=p->next; } return p; } int length() { int len=0; node* p=first(); while (p!=NULL) { len++; p=p->next; } return len; } void print() { node* p=head->next; while (p!=NULL) { cout<<p->data<<"-->"; p=p->next; } cout<<"#"<<endl; } ~List() { while (head->next!=NULL) { node* p=head->next; head->next=p->next; delete p; } delete head; } }; #endif
2、求单链表倒数第m个节点
思路:
设置两个指针,一个指针指向单链表第1个节点,第二个指针指向单链表第m个节点,两个指针同时开始步进,
当第二个指针指向单链表最后一个节点时,第一个指针指向单链表倒数第m个节点
#ifndef MBackword_H #define MBackword_H #include "List.h" /** * 求解单链表倒数第m个节点 */ int Mbackword(List* p,int m) { node* plist=p->first(); node* ptr=p->first(); int i=1; //取得第m个节点 /*while (ptr!=NULL && i<m) { ptr=ptr->next; i++; }*/ ptr=p->getPtr(m); //cout<<"data:"<<ptr->data<<endl; //两个指针同时遍历,ptr[从第m个位置开始遍历]到达链表尾部时,plist指向倒数第m个节点 while (ptr->next!=NULL) { ptr=ptr->next; plist=plist->next; } return plist->data; } #endif
3、求两个单链表是否相交,若相交求交点
思路:
如果两个单链表相交,那么两个单链表的最后一个节点一定相同,可以对两个单链表进行遍历,比较两个链表最后一个节点的地址是否相同,
若相同则两个单链表相交。
求相交的节点,可以先计算两个单链表的长度分别为LenA和LenB,如果LenA>LenB ,那么,单链表A从第(LenB-LenA)开始遍历,单链表B
从第一个节点开始遍历,当两个指针相同时,即为两个单链表的交点。对于LenA<LenB的情况也相同。
#ifndef _INTERSECTION_H #define _INTERSECTION_H /* * 判断两链表是否相交,若相交求出交点 */ //判断是否相交 bool isIntersection(List* listA,List* listB) { bool intersect=false; node* pa=listA->last(); node* pb=listB->last(); if (pa==pb) { intersect=true; } return intersect; } //返回交点 int intersection(List* listA,List* listB) { int value=-1; //相交时,存储相交节点的数值,否则为-1 if (isIntersection(listA,listB)) { int lenA=listA->length(); int lenB=listB->length(); node* pa=listA->first(); node* pb=listB->first(); int distance=lenA-lenB; if (distance>0) //ListA比较长 { node* pa=listA->getPtr(distance+1); while (pa!=pb) { pa=pa->next; pb=pb->next; } value=pa->data; } else //ListB比较长 { node* pb=listB->getPtr(-distance+1); while (pa!=pb) { pa=pa->next; pb=pb->next; } value=pa->data; } } return value; } #endif
4、单链表原地逆序
思路:
一次遍历单链表,将单链表逆序。使用三个指针,分别指向单链表的第1个、第2个、第3个节点,第3个节点不为NULL时,进行while循环,每次循环过程中,修改第2个指针指向节点的next节点为第1个指针指向的节点,然后修改三个指针的位置,进行下一次遍历。
1、判断单链表是否有环,如果有环则求解环的入口点
思路:
判断单链表是否有环可以使用两个指针,快指针和慢指针,慢指针步进长度为1,快指针步进长度为2,遍历单链表当快指针
不为NULL时,快指针与慢指针相等则单链表存在环,否则,不存在环。
单链表存在环的情况下,求解环的入口,可以进行如下的计算:
1.1 首先求解使用快慢指针,单链表相交的节点。
1.2 假设单链表环的长度为r,单链表第一个节点到环的入口点长度为a,若慢指针走了S步,则快指针走了2S步,
存在如下的等式:2S=S+nr; (其中n 为快指针绕环的圈数,r为环的长度 ),也就是S=nr
1.3 假设快慢指针相交点,距离环入口点的距离为x ,那么存在如下等式:
S=a+x; 由于 S=nr 也就是 a = nr – x =( n-1 )r +( r – x )
这说明,单链表第1个指针从第1个节点开始遍历,第2个指针从快慢指针相遇的地方,开始遍历,步进长度为1
最终两个指针会在单链表的入口点相遇。
#ifndef _ISLOOP_H #define _ISLOOP_H /* * 单链表是否有环 */ node* isLoop(List* list) { node* pmeet=NULL; node* slow=list->first(); //慢指针,步进长度为1 node* fast=list->first(); //快指针,步进长度为2 while(fast!=NULL) { //先步进,防止初始时slow==fast slow=slow->next; fast=(fast->next!=NULL?fast->next->next:NULL); if (slow==fast) { pmeet=slow; break; } } return pmeet; } /* * 求解单链表环的入口 */ int entrace(List* list) { int data=-1; node* pmeet=isLoop(list); if (pmeet!=NULL) //存在环的情况 { node* ptr=list->first(); while(ptr!=pmeet) { ptr=ptr->next; pmeet=pmeet->next; } data=ptr->data; } return data; } #endif