代码改变世界

链表练习

2010-12-22 22:32  Clingingboy  阅读(888)  评论(0编辑  收藏  举报

一.单链表

基本数据结构预留

#include "stdafx.h"
#include <iostream>
using namespace std;
#include <string>
#include <queue>
class Link
{
public:
Link(int iata)
{
iData=iata;
}
int iData;
Link* Next;
void DisplayLink() {
cout << iData << endl;
}
};
class LinkList
{
private:
Link* first;
public:
void InsertFirst(int iData)
{
Link* newLink=new Link(iData);
if(IsEmpty())
{
first=newLink;
first->Next=NULL;
} else
{
newLink->Next=first;
first=newLink;
}
}
bool IsEmpty()
{
return first==NULL;
}
Link* GetFirstNode()
{
return first;
}
Link* GetLastNode()
{
Link* currentLink=first;
while(currentLink->Next&& !IsEmpty())
currentLink=currentLink->Next;
return currentLink;
}
LinkList(){first=NULL;}
}

1.删除值为n的节点的直接前驱节点

p=>r=>n变为p=>r

思路:由于是单链表没有前驱,所以要提前记录,并且在寻找后继的过程也要做记录,若找到则将更改节点的前驱的前驱的后继(即节点前驱)

void DeleteBefore(int iData)
{
//default val
Link* p=first;
Link* q=first->Next;
Link* r;
if(q!=NULL)
r=q->Next;
//find
while(r!=NULL && r->iData!=iData)
{
p=q;
q=r;
r=r->Next;
}
//delete
if(r!=NULL)
{
p->Next=q->Next;
//p->Next=r;
delete q;
}
}

Test

LinkList list;
list.InsertFirst(4);
list.InsertFirst(1);
list.InsertFirst(2);
list.InsertFirst(3);
list.DeleteBefore(1);

2.设置C={a1,b1,a2,b2…,an,bn}以hc单链表存放,将其拆分为2个链接({a1,a2…,an}{b1,b2…,bn}),可以理解为奇偶拆分

static void Split(LinkList* ha,LinkList* hb,LinkList* hc)
{
//3,2,1,4=>3,2 1,4
Link* p=hc->GetFirstNode();
Link* ra;
Link* rb;
while(p!=NULL)
{
//add first link
if(ha->IsEmpty())
{
ha->InsertFirst(p->iData);
ra=ha->GetFirstNode();
} else
{
ra->Next=p;
ra=p;
}
//add second link
p=p->Next;
if(p!=NULL)
{
if(hb->IsEmpty())
{
hb->InsertFirst(p->iData);
rb=hb->GetFirstNode();
}
else
{
rb->Next=p;
rb=p;
}
p=p->Next;
}
}
//remove end link's next
ra->Next=rb->Next=NULL;
}

3.将两个有序单链表归并为一个有序单链表

思路:与顺序表其实是一样的,前提还是有序不重复,最后的时候记得有没有合并的加一个链接,这里的效率好一些.

static void Merge(LinkList* ha,LinkList* hb,LinkList* hc)
{
Link* p=ha->GetFirstNode();
Link* q=hb->GetFirstNode();
Link* r;
while(p!=NULL && q!=NULL)
{
if(p->iData<q->iData)
{
if(hc->IsEmpty())
{
hc->InsertFirst(p->iData);
r=hc->GetFirstNode();
} else
{
r->Next=p;
r=p;
}
p=p->Next;
} else if(p->iData>q->iData)
{
if(hc->IsEmpty())
{
hc->InsertFirst(q->iData);
r=hc->GetFirstNode();
} else
{
r->Next=q;
r=q;
}
q=q->Next;
}
}
if(q!=NULL)
{
p=q;
}
if(p!=NULL)
{
hc->GetLastNode()->Next=p;
}
}

4.设置单链表L为递增有序,设计一个算法删除表中值大于min或者小于max的节点

由于是有序的,只要找出第一个大于min的节点和小于max的节点,然后连接起来就可

因为没有前驱,还是需要一个暂存变量

void SortOutRange(int min,int max)
{
//1,2,3,4,5,6=>1,2,6
Link* start=this->GetFirstNode();
Link* temp=start;
while(temp!=NULL && temp->iData<min)
{
start=temp;
temp=temp->Next;
}
Link* end=temp;
while(end!=NULL && end->iData<=max)
{
end=end->Next;
}
start->Next=end;
//free
}

5.同上,但链表改为非有序

由于不是有序,那么就导致若每次遇到范围内则删除之,即有删除就要移位.复杂度为O(n)

这里本应该记录被删除节点的前驱的,偷懒省去了

void OutRange(int min,int max)
{
//1,3,2,5,8,6=>1,2,8,6
Link* p;
Link* temp=this->GetFirstNode();
while(temp!=NULL)
{
p=temp;
temp=temp->Next;
if(p->iData>=min && p->iData<=max)
{
this->DeleteLink(p);
}
}
//free
}

6.有序递增,删除值域重复的结点

由于是有序,那么值域重复肯定是相邻的

void SortDistinct()
{
//1,2,2,4,5,5,6,6=>1,2,4,5,6
Link* p=this->GetFirstNode();
if(p==NULL) return;
while(p->Next!=NULL)
{
if(p->iData==p->Next->iData)
{
p->Next=p->Next->Next;
} else
{
p=p->Next;
}
}
}

7.同上,非有序删除值域重复的结点(复杂度为O(n的平方)

无序的话,就得从第1个节点与其他节点都比较一遍,每次循环则减少一次比较的次数

比如1,2,3=>1,2 1,3=>2,3(因为1,2比较过了,即取无重复的2个数字组合)

void Distinct()
{
//1,2,2,4,5,5,6,6=>1,2,4,5,6
Link* p=this->GetFirstNode();
Link* r;
Link* temp;
while(p!=NULL)
{
r=p->Next;
while(r!=NULL)
{
temp=r;
r=r->Next;
if(p->iData==temp->iData)
{
this->DeleteLink(temp);
}
}
p=p->Next;
}
}

都是删除的方法,思路还是很相似的

8.判定一个单链表是否单调递增

很简单只要一个节点的后驱比其自身小的话就算不是递增

bool IsIncrease()
{
Link* p=this->GetFirstNode();
while(p->Next!=NULL)
{
if(p->iData>p->Next->iData)
return false;
else
p=p->Next;
}
return true;
}

9.为单链表排序单调递增

即冒泡排序

10.删除单链表最小值

即找出最小值,一遍比较一遍存,然后删除即可,关键同时还要记录其前驱

记录一组寻常的节点和其前驱和最小值的和其前驱

void Deletemin()
{
Link* pre=this->GetFirstNode();
Link* p=pre->Next;
Link* min=p;
Link* minpre=pre;
while(p!=NULL)
{
//min link and its pre link
if(p->iData<min->iData)
{
min=p;
minpre=pre;
}
//pre and current link
pre=p;
p=p->Next;
}
minpre->Next=min->Next;
delete min;
}

11.倒置(reverse)

解法1:用stack,练习用的

void Reverse()
{
InsertFirst(1);
InsertFirst(2);
InsertFirst(3);
//放入queue,先进后出
queue<int> stack;
Link* currentLink=this->GetFirstNode();
Link* lastLink=this->GetLastNode();
while(currentLink!=NULL)
{
stack.push(currentLink->iData);
currentLink=currentLink->Next;
}
//3,2,1
this->ClearList(first);
first=NULL;
while(!stack.empty())
{
int a=stack.front();
this->InsertFirst(a);
stack.pop();
}
currentLink=this->GetFirstNode();
}

解法2 就地倒置

思路:
1.因为是就地倒置,所以每个向前的元素每次都成为第一个元素
2.然后将该元素的前驱指向原先的第一个元素

void Reverse1()
{
    //4,3,2,1=>1,2,3,4
    //3,4,2,1
    //2,3,4,1
    //1,2,3,4
    Link* prev=first;
    Link* r;
    first=NULL;
    while(prev!=NULL)
    {
        //r is temp store=>prev next
        r=prev->Next;
        //exchange preve and next
        prev->Next=first;
        //change firt link
        first=prev;
        //change prev link
        prev=r;
    }
}

方案2:

基于上面的基础,把原链表的尾结点暂存起来,而不是每次更改首结点,等循环结束以后给首结点赋值,其他保持不变

void Reverse2()
{
    //4,3,2,1=>1,2,3,4
    //3,4,2,1
    //2,3,4,1
    //1,2,3,4
    Link* prev=first;
    Link* current=prev->Next;
    prev->Next=NULL;
    Link* r;
    while(current!=NULL)
    {
        //r is temp store=>prev next
        r=current->Next;
        //exchange preve and next
        current->Next=prev;
        //change firt link
        prev=current;
        //change prev link
        current=r;
    }
    first=prev;
}

12.两个集合的交、差、并集

1