数据结构——单链表排序

数据结构——单链表排序

昨天晚上写了个单链表排序功能的函数,没想到数据结构课只是过了一学期,写单链表数据结构的函数就遇到太多问题。唉,S老师,我对不起你啊。

我所要写的函数的程序是以单链表结构进行组织,无空头节点。

要求可以按节点里的某个属性作为key大小排序。

最开始,我并没回想到这个单链表交换位置,需要以指针进行移动。事实上,潜意识认为,只要将指针的值进行交换,即可(完全忘了单链表的知识了)。因为很自然的想成,链表是通过指针进行寻找节点的,交换了指针,就相当于交换节点顺序。貌似没有问题,所以我编写了如下函数(为了卖弄下,我还挑了个麻烦的快速排序方法,而不是后来简单的冒泡排序):

void QuickSort(struct book *head_book,int low,int high)

{

static int count=1;

int i=low,j=high;

struct book  *temp_ptr_book,*iptr_book,*jptr_book;

temp_ptr_book=GetPtr_book(head_book,low);

iptr_book=GetPtr_book(head_book,i);

jptr_book=GetPtr_book(head_book,j);

if(i<j)

{

while(i<j){

while (i<j&&GetKey(temp_ptr_book)>=GetKey(jptr_book))

{jptr_book=GetPtr_book(head_book,j);j--}

if(i<j){iptr_book=jptr_book;i++;}

while (i<j&&GetKey(temp_ptr_book)<GetKey(iptr_book))

{iptr_book=GetPtr_book(head_book,i);i++;}

if(i<j){jptr_book=iptr_book;j--;}

}

iptr_book=temp_ptr_book; 

QuickSort(head_book,low,i-1);  

QuickSort(head_book,i+1,high);  

}

}

运行结果却发现根本没有排序。

因为上面的想法是大错的,我将指针的值进行交换,并未改变节点的交换。指针的值是一个地址,交换值,只改变了指针所指向的是那个节点,但节点之间是靠指针联系的,交换指针值,根本没有任何作用。链表还是那个链表,一点也没有改变。那么就对节点进行修改就好了。

确实这样做了,写了个类似拷贝构造函数,但是测试还是有问题,为什么?因为节点本身是有指向下一个节点的指针的,交换后怎么修改指针呢?

顺着想下去,是不是可以这样想,我不改变链表节点的本身的顺序(即指向下一个节点的指针的值不改变),把节点的值进行交换(赋值),这样也是进行排序了。但是无奈能力太差,不知哪里写的有问题,测试结果出现指针异常。(即使成功如果节点数据太多,拷贝过去这样的时间代价也是极大的!最后有解决方法比这样更好,所以就没去再找这个问题的原因,但从似乎逻辑上这样是可行的。)实在厌烦了,还是先试试简单的冒泡排序吧。

但是没想到冒泡排序也出现了问题。

记得学单链表的时候,交换节点只要一定顺序三次赋值就可以了,但是测试结果,排序结果第一遍就没走完,数据竟然遗失了。又得仔细找原因,后来才发现,一般冒泡排序,是针对基本数据类型或对象的,指针冒泡有些细节仍然要注意:每轮冒泡都是从头节点开始的,但是头节点可能会改变呀。但是我们每次冒泡是都是直接赋值最开始的那个头节点,这无疑会出现问题。普遍冒泡排序方法如下:

//冒泡排序

template <class Type>

void BubbleSort(sortlist<Type> &table)

{

int i=1;int finish=0;

cout<<"冒泡排序结果:\n";

cout<<"初始序列:";table.Show();cout<<endl;

while(i<table.CurrentSize&&!finish)

{

finish=1;  //交换标志

for(int j=0;j<table.CurrentSize-i;j++)

if(table.Arr[j].GetKey()>table.Arr[j+1].GetKey())

{

table.Swap(table.Arr[j],table.Arr[j+1]);

finish=0;    //已交换

}

cout<<"第"<<i<<"趟结果:";

table.Show();cout<<endl;

i++;

}

}

for(int j=0;j<table.CurrentSize-i;j++)中j如果改变成指针,错误想法就是j=head_node;

那么需要怎么解决?,显然我们需要增加一个空头节点,这样头节点的交换就没有问题了。现在给出一般的指针冒泡排序算法(C语言):

//冒泡排序

struct book * BubbleSort(struct book *head_book){

int finish=0;

int size_book=sizeof(struct book); 

struct book *NullH_book=(struct book *)malloc(size_book); //增加一个空头节点

NullH_book->book_next=head_book;

struct book *tail=NULL;

//struct book *j1_ptr_book=head_book->book_next;

while(NullH_book->book_next!=tail&&!finish)

{

finish=1;  //交换标志

struct book *pre_book=NullH_book;

    struct book *cur_book=pre_book->book_next;

while(cur_book!=tail&&cur_book->book_next!=tail){

if(GetKey(cur_book)<GetKey(cur_book->book_next))

{  

pre_book->book_next=cur_book->book_next;

cur_book->book_next=cur_book->book_next->book_next;

pre_book->book_next->book_next=cur_book;

finish=0;    //已交换

}

pre_book=pre_book->book_next;

cur_book=pre_book->book_next;

}tail=cur_book;

}

return NullH_book->book_next;

}

 

 貌似问题解决了,但是这里只给出了冒泡排序,我们知道冒泡排序算法的时间上界为O(n2),即使,我增加了交换标志,可以改进一些,但是我们仍然像可不可以有更好的方法提高排序效率。

你也知道当然存在更好的算法,比如之前的快速排序,此外还有:直接插入排序,简单选择排序,堆排序,二路归并排序等等。我们是不是能够也对单链表排序呢?

但是有一个问题,还是指针问题,冒泡是对相邻的节点交换,指针的交换就很麻烦了,还要多设置一个前一个节点的指针。那么,快速排序呢,是不相邻的节点的赋值(交换),这样的复杂过程,需要设置几个指针,需要怎样移动,要考虑哪些情况。堆排序情况是怎样的?归并排序又要怎样写?

也许是有办法的,但想想也很麻烦,单链表操作实在很不好使用这些排序。是不是真的无法排序呢?办法还是有的。能不能利用已经写过的这些函数进行排序?教材有这些方法的关键代码,课设时也实现过了,但是是对输入的整数,装入一个数组进行排序的,链表节点型的数据怎么排序?

其实,回到最开始的错误想法来看,可以解决这个问题。如果我存放的是指针数组,每个指针指向链表的一个节点。可以通过指针找到节点,比较key,再对指针数组进行排序,这样就实现了对节点的排序。当然只进行这一步,肯定还是错误的,我们还是没对链表进行改变。所以,最后一步就是,在排好序的基础上,走一轮,将指针的指向的节点看成是独立的节点,修改它的属性中的指向下一节点的指针值为当前数组指针的后一个数组指针。这样就重新得到了一个单链表。

唉,用进废退,是不变的法则啊,本以为数据结构学的还可以,现在好久不用了就将数据结构的特点给遗忘了。现在用JAVA,C#,不写C++了指针都用不熟了,经常报错,用C里面的部分方法也出现错误。花了这么久终于算是解决了。

附: 通用排序模板

#include<iostream>

#include<string>

using namespace std;

const int MaxSize=100;

template<class Type>

class element

{

    template<class Type>

friend class sortlist;

private:

Type key; //数据元素关键字

public:

element():key(0){}  //构造函数

Type GetKey(){return key;}  //取关键字

void setKey(const Type k){key=k;}  //修改关键字

element<Type> & operator =(element<Type>&x){key=x.key;return *this;}//赋值,重载运算符=

};

 

template<class Type>

class sortlist{

// template<class Type> friend void QuickSort(sortlist<Type> &table,int low,int high);也可以

friend void InsertionSort(sortlist<Type> &table);   //直接插入排序

friend void BubbleSort(sortlist<Type> &table);      //冒泡排序

friend void SelectSort(sortlist<Type> &table);      //简单选择排序

friend void QuickSort(sortlist<Type> &table,int low,int high);  //快速排序

template<class Type>

friend class MaxHeap;    //堆

friend void HeapSort(sortlist<Type> &table);    //堆排序

friend void merge(sortlist<Type> &sourceTable,sortlist<Type> & mergedTable,

   const int left,const int mid,const int right);  //归并

friend void MergePass(sortlist<Type> &sourceTable,sortlist<Type> & mergedTable,

   const int len);     //一趟归并

friend void MergeSort(sortlist<Type> &table);  //两路归并

public:

element<Type> *Arr;  //存储数据元素的向量

int CurrentSize;  //数据表中的元素个数

 

sortlist():CurrentSize(0){Arr=new element<Type>[MaxSize];}//构造函数

sortlist(const sortlist &sl)  //拷贝构造函数

{

Arr=new element<Type>[MaxSize];CurrentSize=sl.CurrentSize;

for(int i=0;i<CurrentSize;i++)

Arr[i]=sl.Arr[i];

}

~sortlist(){delete [] Arr;}//析构函数

void Swap(element<Type>& x,element<Type> & y)  //交换x和y

{element<Type> temp=x;x=y;y=temp;}

void Insert(const Type k){Arr[CurrentSize].setKey(k); CurrentSize++;}  //插入k

void Show()                  //显示数据元素

{

for(int i=0;i<CurrentSize;i++)

   cout<<Arr[i].GetKey()<<"  ";

}

};

//直接插入排序---使用监视哨改进需要将第一个空出来,此处不便于其他排序算法的使用,所以不使用

template <class Type>

void InsertionSort(sortlist<Type> &table)

{

element<Type> temp;

cout<<"直接插入排序结果:\n";

cout<<"初始序列:";table.Show();cout<<endl;

int j;

for(int i=1;i<table.CurrentSize;i++)

{

temp=table.Arr[i];j=i;

while(j>=0&&temp.GetKey()<table.Arr[j-1].GetKey())

{

table.Arr[j]=table.Arr[j-1];

j--;

}

table.Arr[j]=temp;

cout<<"第"<<i<<"趟结果:";

table.Show();cout<<endl;

}

}

//冒泡排序

template <class Type>

void BubbleSort(sortlist<Type> &table)

{

int i=1;int finish=0;

cout<<"冒泡排序结果:\n";

cout<<"初始序列:";table.Show();cout<<endl;

while(i<table.CurrentSize&&!finish)

{

finish=1;  //交换标志

for(int j=0;j<table.CurrentSize-i;j++)

if(table.Arr[j].GetKey()>table.Arr[j+1].GetKey())

{

table.Swap(table.Arr[j],table.Arr[j+1]);

finish=0;    //已交换

}

cout<<"第"<<i<<"趟结果:";

table.Show();cout<<endl;

i++;

}

}

//简单选择排序

template<class Type>

void SelectSort(sortlist<Type> &table)

{

int k;

cout<<"简单选择排序结果:\n";

cout<<"初始序列:";table.Show();cout<<endl;

for(int i=0;i<table.CurrentSize-1;i++)

{

k=i;

for(int j=i+1;j<table.CurrentSize;j++)

if(table.Arr[j].GetKey()<table.Arr[k].GetKey())

k=j;  //当前最小关键字的位置

if(k!=i)

table.Swap(table.Arr[i],table.Arr[k]);

cout<<"第"<<i+1<<"趟结果:";

table.Show();cout<<endl;

}

}

//快速排序

template <class Type>

class QSort

{

public:

void QuickSort(sortlist<Type> &table,int low,int high);

};

template<class Type>

void QSort<Type>::QuickSort(sortlist<Type> &table,int low,int high)

{

static int count=1;

if(count==1)

{

cout<<"快速排序结果:\n";

cout<<"初始序列:";table.Show();cout<<endl;

}

int i=low,j=high;

element<Type>temp=table.Arr[low];// 取区间第一个位置为基准位置

if(i<j)

{

while(i<j){

while(i<j&&temp.GetKey()<table.Arr[j].GetKey()) j--;

if(i<j){table.Arr[i]=table.Arr[j];i++;}

while(i<j&&temp.GetKey()>=table.Arr[i].GetKey())i++;

if(i<j){table.Arr[j]=table.Arr[i];j--;}

}

table.Arr[i]=temp;  //基准元素归为

cout<<"第"<<count<<"趟结果:";table.Show();cout<<endl;count++;

QuickSort(table,low,i-1);  //左区间递归快速排序

QuickSort(table,i+1,high);  //右区间递归快速排序

}

}

//堆排序

template<class Type>

class MaxHeap{

public:

MaxHeap(int maxSize);  //构造函数,建立空堆

MaxHeap(Type *a,int n);  //构造函数,以数组a[]的值建立堆

~MaxHeap(){delete[] heapArr;

}  //析构函数

int Insert(Type &d);  //在堆中插入元素d

Type DeleteTop();  //删除堆顶元素

Type GetTop()const  //取得堆顶元素

{return  heapArr[0];}

int IsEmpty()   //判断堆是否为空

{return heapCurrentSize==0;}

int IsFull() const  //判断堆是否为满

{return heapCurrentSize==heapMaxSize;}

void SetEmpty(){heapCurrentSize=0;}//置堆为空

void HeapSort();  //堆排序

void Show(int k)

{

for(int i=0;i<k;i++)

cout<<heapArr[i].GetKey()<<"  ";

}

private:

    int heapMaxSize;  //堆中数据元素的最大数目

Type *heapArr;  //存放堆中数据元素的数组

int heapCurrentSize;  //堆中当前元素数目

void FilterDown(int p); //向下调整使以结点p为根的子树成为堆

void FilterUp(int p); //  向上调整使以结点p为根的子树成为堆

};

 

template<class Type>

int MaxHeap<Type>::Insert(Type &d)

{

if(IsFull())

{cout<<"堆已满"<<endl;return 0;}

heapArr[heapCurrentSize]=&d//在堆尾插入数据元素d

FilterUp(heapCurrentSize);  //向上调整为堆

heapCurrentSize++;

return 1;

}

 

template<class Type>

MaxHeap<Type>::MaxHeap(int maxSize){

//根据maxSize,建立空堆

if(maxSize<=0)

{

cerr<<"堆的大小不能小于1"<<endl;

exit(1);

}

heapMaxSize=maxSize;

heapArr=new Type[heapMaxSize]; // 建立堆空间

heapCurrentSize=0;

}

template<class Type>

MaxHeap<Type>::MaxHeap(Type *a,int n){  //根据数组a[]中的数据,建立堆,n为数组的大小

if(n<=0)

{cerr<<"堆的大小不能小于1"<<endl;

exit(1);

}

heapMaxSize=n;  //确定堆的最大空间

heapArr=new Type[heapMaxSize]; //创建堆空间

//heapArr=a;     //?直接赋值?

for(int i=0;i<n;i++)

heapArr[i]=a[i];

heapCurrentSize=n;  //当前堆大小

/*int i=(heapCurrentSize-2)/2;  //找最后一个分支结点的序号

while(i>=0)

{

FilterDown(i);  //从开始到heapCurrentSize-1为止进行调整

i--;

}*/

}

template<class Type>

void MaxHeap<Type>::FilterDown(const int start)

{

//向下调整从start开始到heapCurrentSize-1为止的子序列成为最大堆

int i=start,j;

Type temp=heapArr[i];

if(heapCurrentSize<=2)

{

if(i==0&&heapCurrentSize==2&&heapArr[0].GetKey()<heapArr[1].GetKey())

{heapArr[0]=heapArr[1];heapArr[1]=temp;}

}

else{

j=2*i+1;    //j为i的左孩子

while(j<heapCurrentSize){

 if(j<heapCurrentSize-1&&heapArr[j].GetKey()<heapArr[j+1].GetKey())

 j++;  //从左右孩子中选较大者

 if(temp.GetKey()>=heapArr[j].GetKey()) break;

 else {

heapArr[i]=heapArr[j];

 i=j;j=2*j+1;}

}

heapArr[i]=temp;

}

}

template<class Type>

Type  MaxHeap<Type>::DeleteTop()

{

if(IsEmpty())

{cout<<"堆已空"<<endl;return NULL;}

Type temp=heapArr[0]; 

heapArr[0]=heapArr[heapCurrentSize-1];   //堆末元素移到堆顶

heapCurrentSize--;

FilterDown(0);//自顶向下调整堆

return temp;

}

template<class Type>

void  MaxHeap<Type>::HeapSort()

{

int tablesize=heapCurrentSize,j=1;

cout<<"堆排序结果:\n";

cout<<"初始序列:";Show(tablesize);cout<<endl;

for(int i=(heapCurrentSize-2)/2;i>=0;i--)

FilterDown(i);   //初始建堆

 

for(int i=heapCurrentSize-1;i>=1;i--)

{

//table.Swap(table.Arr[0],table.Arr[i]);   //堆顶与最后一个元素交换

Type temp=heapArr[0];

heapArr[0]=heapArr[i];

heapArr[i]=temp;

heapCurrentSize--;

FilterDown(0);   //重建最大堆

    

cout<<"第"<<j<<"趟结果:";j++;

Show(tablesize);cout<<endl;

}

heapCurrentSize =tablesize;

}

//归并排序

template <class Type>

class MGSort

{

public:

void merge(sortlist<Type> &sourceTable,sortlist<Type> & mergedTable,

   const int left,const int mid,const int right);

void MergePass(sortlist<Type> &sourceTable,sortlist<Type> & mergedTable,

   const int len);

void MergeSort(sortlist<Type> &table);

};

template<class Type>

void MGSort<Type>::merge(sortlist<Type> &sourceTable,sortlist<Type> & mergedTable,

   const int left,const int mid,const int right)

{

int i=left,j=mid+1,k=left;  //指针初始化

while(i<=mid&&j<=right)

if(sourceTable.Arr[i].GetKey()<=sourceTable.Arr[j].GetKey())

{mergedTable.Arr[k]=sourceTable.Arr[i];i++;k++;}

else{mergedTable.Arr[k]=sourceTable.Arr[j];j++;k++;}

if(i<=mid)

for(int p=k,q=i;q<=mid;p++,q++)

mergedTable.Arr[p]=sourceTable.Arr[q];

else

for(int p=k,q=j;q<=right;p++,q++)

mergedTable.Arr[p]=sourceTable.Arr[q];

}

//一趟归并

template<class Type>

void  MGSort<Type>::MergePass(sortlist<Type> &sourceTable,sortlist<Type> & mergedTable,

   const int len)

{

int i=0,CurrentSize=sourceTable.CurrentSize+mergedTable.CurrentSize;

while(i+2*len<=CurrentSize-1)

{

merge(sourceTable,mergedTable,i,i+len-1,i+2*len-1);

i+=2*len;

}

if(i+len<=CurrentSize-1)

merge(sourceTable,mergedTable,i,i+len-1,CurrentSize-1);

else

for(int j=i;j<=CurrentSize-1;j++)

mergedTable.Arr[j]=sourceTable.Arr[j];

}

//两路归并

template<class Type>

void  MGSort<Type>::MergeSort(sortlist<Type> &table)

{

sortlist<Type> tempTable;

int len=1,i=1;

cout<<"归并排序结果:\n";

cout<<"初始序列:";table.Show();cout<<endl;

while(len<table.CurrentSize)

{

MergePass(table,tempTable,len); len*=2;

cout<<"第"<<i<<"趟结果:";

table.Show();cout<<endl;i++;

MergePass(tempTable,table,len); len*=2;

cout<<"第"<<i<<"趟结果:";i++;

table.Show();cout<<endl;

}

//delete[] tempTable;

}

void bound()

{

cout<<"\n===================================================================\n";

}

void main()

{

string choice;bool flag=true;

char ch;

    sortlist<int> SL;int dain,size;

bound();

cout<<"                      欢迎使用\n";

 cout<<"请选择排序数目(<=100):";cin>>size;cout<<endl;

 while(size>MaxSize)

 {

 cout<<"超过了排序数目,请重新输入!\n";cin>>size;

 }

 cout<<"请输入序列:\n";

 for(int i=0;i<size;i++)

 {  cin>>dain;

 SL.Insert(dain);

 }

 bound();

do

{

cout<<"\n===================================================================\n";

cout<<"\n                          排序算法的演示\n";

cout<<"\n                1. 直接插入排序演示\n";

cout<<"\n                2. 冒泡排序演示\n";

cout<<"\n                3.      简单选择排序演示\n";

cout<<"\n                4. 快速排序演示\n";

cout<<"\n                5.      堆排序演示\n";

cout<<"\n                6. 归并排序演示\n";

cout<<"\n                7.      离开 \n";

cout<<"\n===================================================================\n";

cout<<"请输入要选择程序功能:";

cin>>choice;

sortlist<int> tSL(SL); MGSort<int> M;QSort<int> Q;MaxHeap<element<int> > MH(tSL.Arr,tSL.CurrentSize);

if(choice.length()>1)

ch='0';

else

 ch=choice[0];

switch(ch)

{

case '1': bound();InsertionSort<int>(tSL);bound();break;

case '2': bound();BubbleSort<int>(tSL);bound();break;

case '3': bound();SelectSort<int>(tSL);bound();break;

case '4': bound();Q.QuickSort(tSL,0,size-1);bound();break;  

case '5': bound();MH.HeapSort();bound();break;///bug:MaxHeap<Type>::HeapSort”: 不能将参数 1 从“sortlist<Type>”转换为“sortlist<Type>

case '6': bound();M.MergeSort(tSL);bound();break;

case '7':flag=false;break;

default:cout<<"请重新输入。\n";

}

}while(flag);

 

}

 

 

 

 

 

 



转载于:https://my.oschina.net/tianjiqx/blog/326750

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值