单链表作为一种常见的数据结构,在程序设计中很重要。我也是在最近参加的一些面试,感觉好多公司在技术面试时都会问一些单链表的问题,在此进行一下总结。
本文的完整代码在:https://github.com/JayYangSS/Data-Structure-and-Algorithm/tree/master/Linklist
首先建立单链表的基本数据单元:
typedef struct Node
{
int data;
Node *next;
}Node;
声明单链表的类,将单链表的相关操作均封装到该类中,后续的单链表操作方法还会继续更新,下面得1声明在LinkList.h文件中:
class LinkList
{
public:
LinkList(void);
~LinkList(void);
Node* SetupLinkList(void);
// //diaplay the data of link list
void diaplay(Node* head);
// 输入数值,建立有序的链表
Node* SetupOrderedLinkList(bool down);
// 将元素插入到pp所指节点后
void Insert(Node* pp,Node *newNode);
// 将两个单链表按顺序合并为一个单链表
Node* Merge(Node* first, Node* second);
// 将链表复制到另一条链表上
void copyto(Node* src, Node* dst);
// 合并两条有序链表的方法二
Node* Merge2(Node* first, Node* second);
// 寻找单链表的中间节点
Node* FindMid(Node* head);
// 使用归并排序法实现链表排序
Node* SortLinkList(Node* head);
};
相应的实现如下:
// 将链表复制到另一条链表上
void LinkList::copyto(Node* src, Node* dst)
{
Node *p,*q,*tmp;
q=new Node;
dst->next=q;
p=src->next;
do
{
q->data=p->data;
p=p->next;
if(p!=NULL){
tmp=new Node;
q->next=tmp;
q=q->next;
q->next=NULL;
}
}while(p!=NULL);
}
<pre name="code" class="cpp">Node* LinkList::SetupLinkList(void)
{
Node *head,*p,*q;
cout<<"input the data(input '-1' to end):"<<endl;
head=new Node;
head->next= NULL;
q=head->next;
while (1)
{
int a;
cout<<"input data:";
cin>>a;
if (a==-1)break;
else{
p=new Node;
if (head->next==NULL)
{
head->next=p;
p->data=a;
p->next=NULL;
q=p;
}
else
{
p->data=a;
q->next=p;
q=p;
q->next=NULL;
}
}
}
return head;
}
<pre name="code" class="cpp">// //diaplay the data of link list
void LinkList::diaplay(Node* head)
{
Node *p;
if (head == NULL)
cout << "The head of the LinkList is NULL!" << endl;
else if (head->next==NULL)
{
cout<<"Linklist is empty!"<<endl;
}
else{
p=head->next;
while(p!=NULL)
{
cout<<p->data<<endl;
p=p->next;
}
}
}
// 将元素插入到pp所指节点后
void LinkList::Insert(Node* pp,Node *newNode)
{
if (pp->next==NULL)
{
pp->next=newNode;
newNode->next=NULL;
}
else{
Node *tmp=pp->next;
pp->next=newNode;
newNode->next=tmp;
}
}
建立有序单链表的程序如下:
Node* LinkList::SetupOrderedLinkList(bool down)
//down=true时为降序排列,down=false时为升序排列
{
Node *head,*p,*q;
cout<<"input the data(input '-1' to end):"<<endl;
head=new Node;
head->next= NULL;
q=head->next;
while (1)
{
int a;
cout<<"input data:";
cin>>a;
if (a==-1)break;
else{
p=new Node;
p->data=a;
//创建第一个元素
if (head->next==NULL)
{
head->next=p;
p->next=NULL;
q=p;
}
else
{
Node *search,*pre;
search=head->next;
pre=head;
while(pre->next!=NULL)
{
if (down==false)//升序排列
{
//比较待插入链表的数据与当前链表中的元素大小
//若待插入元素大于当前节点,比较下一节点
//若待插入元素小于当前节点,插入到该节点之前
if (a>=search->data)
{
if (search->next==NULL)Insert(search,p);
pre=pre->next;
search=search->next;
}
else
{
Insert(pre,p);
break;
}
}
else//降序排列
{
if (a<=search->data)
{
if (search->next==NULL)Insert(search,p);
pre=pre->next;
search=search->next;
}
else
{
Insert(pre,p);
break;
}
}
}
}
}
}
return head;
}
将两条有序单链表合并为一条有序的单链表,第一种实现方法的思路是首先将其中的一条单链表复制到一条新的链表head上,再从另一条单链表开始遍历,每取出第二条链表上的一个元素,就将其与head链表上的每一个元素进行比较,按顺序插入即可(该思路写的程序有点乱,复杂度大),代码如下:
// 将两个单链表按顺序合并为一个单链表
Node* LinkList::Merge(Node* first, Node* second)
{
if(first->next==NULL)
return second;
else if (second->next==NULL)
return first;
else{
Node *d,*head;//存储合并好的新的链表
Node *p1,*p2,*pre;
head=new Node;
copyto(first,head);//新的链表头
//p1=head->next;
//pre=head;
p2=second->next;
while(p2!=NULL)
//while(((p2->next!=NULL)&&p2!=NULL)|((p2!=NULL)&&(p2->next==NULL)))
{
p1=head->next;
pre=head;
while(pre!=NULL)
{
if ((p2->data>=p1->data)&&(p1->next!=NULL))
{
p1=p1->next;
pre=pre->next;
if ((p1->next==NULL)&&(p2->data>=p1->data))
{
Insert(p1,p2);
p1=p1->next;
pre=pre->next;
p2=p2->next;
break;
}
}
else
{
d=new Node;
d->data=p2->data;
Insert(pre,d);
if (p2->next==NULL){p2=p2->next;break;}
p2=p2->next;
pre=pre->next;
break;
}
}
}
return head;
}
}
第二种合并的方法比第一种简单,思路是:每次取出两条链表的第一个节点中的元素,将其中较小的元素放入一条新的链表中,同时将该条链表的指针后移(相当于将该元素从这条链表中清除出去),再将两条链表中的指针所指元素进行大小比较,重复上述操作。若其中的一条链表中的元素已经全部放入新链表中,而另一条链表还没有放置完毕,则将剩余部分直接放入新链表尾部,得到的新链表即为合并好的有序链表,程序如下:
Node* LinkList::Merge2(Node* first, Node* second)
{
Node *head,*tail;
head=new Node;
tail=head;
first=first->next;
second=second->next;
if(first==NULL)
return second;
if(second==NULL)
return first;
while(first&&second)
{
if (first->data>second->data)
{
Node* tmp=new Node;
tmp->data=second->data;
tail->next=tmp;
tail=tail->next;
tail->next=NULL;
// delete tmp;
second=second->next;
}
else
{
Node* tmp=new Node;
tmp->data=first->data;
tail->next=tmp;
tail=tail->next;
tail->next=NULL;
//delete tmp;
first=first->next;
}
}
if(first==NULL)
{
while(second)
{
Node *tmp=new Node;
tmp->data=second->data;
tail->next=tmp;
tail=tail->next;
tail->next=NULL;
second=second->next;
}
}
else
{
while(first)
{
Node *tmp=new Node;
tmp->data=first->data;
tail->next=tmp;
tail=tail->next;
tail->next=NULL;
first=first->next;
}
}
return head;
}
为了实现将无序单链表进行排序,使用归并排序法,其主要思想如下:
1.首先找到单链表的中点,将其分为两个链表
2.对前后半部分链表进行同样的操作,再分别分为两半部分链表,如此一直重复下去,直到每个链表只含有一个元素,此时每个链表均为有序单链表
3.对每一个有序单链表使用之前实现的合并有序单链表方法进行合并,直到最后所有链表合并为一条,该链表即为重新排序好的单链表
实现方法使用到递归:
// 使用归并排序法实现链表排序
Node* LinkList::SortLinkList(Node* head)
{
if (!head)return NULL;
if (!head->next)return head;
Node *mid,*next=NULL,*head2;
mid=FindMid(head);
if (mid->next != NULL)
{
next = mid->next;
//为后半部分添加链表头
head2 = new Node;
head2->next = next;
mid->next = NULL;
return Merge2(SortLinkList(head), SortLinkList(head2)); //使用递归调用
}
else
return head;
}
Node* LinkList::FindMid(Node* head)
{
if(!head)return NULL;
if(!head->next)return head;
Node *slow,*fast;
slow=head;
fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;//游标slow移动一步,fast移动两步,当fast移动到末尾,则slow正好移动到中点
}
return slow;
}