1. 程式人生 > >C++實現各種排序演算法

C++實現各種排序演算法

//各種排序演算法
#include"stack.h"
//插入排序(直接插入排序和希爾排序)
//直接插入排序
//正確的swap函式應該像下面那樣寫,否則無法實現交換
//對於陣列使用這個函式,swap(a[low], a[high])
void swap(int *a, int *b)  
{  
int tmp = *a;  
*a = *b;  
*b = tmp;  
}  
void print(int num[],int length)
{
if(num!=NULL&&length>0)
{
for(int i=0;i<length;i++)
{
cout<<num[i]<<" ";
}
cout<<endl;
}
}
void insertsort(int num[],int length)
{
if(num==NULL||length<=0)
cout<<"陣列為空"<<endl;
else
{
for(int i=1;i<length;i++)
{
int temp=num[i];
int j=i-1;
while(j>=0&&temp<num[j])
{
num[j+1]=num[j];
j--;
}
num[j+1]=temp;
}
}
}
//希爾排序
void shellsort(int num[],int length)
{
int k=length/2;//步長
if(num==NULL||length<=0)
cout<<"陣列為空"<<endl;
else
{
while(k>=1)
{
for(int i=k;i<length;i++)
{
int temp=num[i];
int j=i-k;
while(j>=0&&temp<num[j])
{
num[j+k]=num[j];
j=j-k;
}
num[j+k]=temp;
}
k/=2;
}
}
}
//選擇排序(直接選擇和堆排序)
//直接選擇排序
void selectsort(int num[],int length)
{
if(num!=NULL&&length>0)
{
for(int i=0;i<length;i++)
{
int index=i;
for(int j=i+1;j<length;j++)
{
if(num[j]<num[index])
index=j;
}
if(index!=i)
{
int temp=num[i];
num[i]=num[index];
num[index]=temp;
}
}
}
}
//堆排序
//採用最大堆
void maxheapify(int num[],int i,int size)
{
int left=2*i+1;int right=2*i+2;int largest=i;
if(left<=size&&num[left]>num[i])
largest=left;
if(right<=size&&num[right]>num[largest])
largest=right;
if(largest!=i)
{
int temp=num[i];
num[i]=num[largest];
num[largest]=temp;
maxheapify(num,largest,size);//避免調整之後以max為父節點的子樹不是堆 
}
}
void buildmaxheap(int num[],int size)
{
for(int i=(size-1)/2;i>=0;i--)//注意這裡需要size-1
maxheapify(num,i,size);//必須從最後一個開始往前推
}
void heapsort(int num[],int size)
{
buildmaxheap(num,size);
for(int i=size-1;i>=0;i--)
{
int temp=num[0];//注意這裡是陣列最後一個元素與陣列首元素交換也就是num[size-1]與num[0]
num[0]=num[i];
num[i]=temp;
maxheapify(num,0,i-1);//注意首元素標號是0
}
}
//交換排序(氣泡排序和快速排序)
//氣泡排序
void bubblesort(int num[],int size)
{
if(num!=NULL&&size>0)
{
for(int i=0;i<size-1;i++)//注意這裡需要是size-1,否則下面size-i-1就會超過範圍了,
for(int j=0;j<size-i-1;j++) //注意這裡是size-i-1.不減去1。j+1就會超過範圍
{
if(num[j]>num[j+1])
{
int temp=num[j];
num[j]=num[j+1];
num[j+1]=temp;
}
}
}
}
//雙向氣泡排序(雞尾酒排序)
void bubblesort1(int num[],int size)
{
if(num!=NULL&&size>0)
{
int high=size-1;
int low=0;
while(low<high)
{
for(int i=low;i<high;i++)
{
if(num[i]>num[i+1])
{
int temp=num[i];
num[i]=num[i+1];
num[i+1]=temp;
}
}
--high;
for(int j=high;j>low;j--)
{
if(num[j]<num[j-1])
{
int temp=num[j];
num[j]=num[j-1];
num[j-1]=temp;
}
}
++low;
}
}
}




//快速排序
//方法1


int partition(int num[],int length,int start,int end)
{
if(num!=NULL&&length>=0&&start>=0&&end<=length&&end>=start)
{
int key=num[end];
int i=start-1;
for(int j=start;j<end;j++)
{
if(num[j]<key)
{
i++;
int temp=num[i];
num[i]=num[j];
num[j]=temp;
}
}
i++;
int temp=num[i];
num[i]=num[end];
num[end]=temp;
return i;
}
}




void quicksort(int num[],int length,int start,int end)
{
if(start==end)//必須要有這個
return;
if(start<end)
{
int index=partition(num,length,start,end);
quicksort(num,length,start,index-1);
quicksort(num,length,index+1,end);
}
}
//快速排序 方法2


int partition1(int num[],int low,int high)
{
int key=num[low];//基準元素
while(low<high)  //從表的兩端交替地向中間掃描  
{
while(low<high&&num[high]>=key)//從high 所指位置向前搜尋,至多到low+1 位置。
high--;
int temp=num[low];//將比基準元素小的交換到低端 
num[low]=num[high];
num[high]=temp;
while(low<high&&num[low]<=key)//從low所指的位置向後搜尋,至多到high-1位置
low++;
int temp1=num[low];//將比基準元素大的交換到後面的位置
num[low]=num[high];
num[high]=temp1;
}
return low;
}
void quicksort1(int num[],int low,int high)
{
if(low<high)
{
int index=partition1(num,low,high);//將表一分為二  
quicksort1(num,low,index-1);//遞迴對低子表遞迴排序 
quicksort1(num,index+1,high);//遞迴對高子表遞迴排序
}
}
//使用非遞迴的快速排序
//其實就是用棧儲存每一個待排序子串的首尾元素下標,下一次while迴圈時取出這個範圍,對這段子序列進行partition操作
/*用棧實現:
1。每次把支點的右段入棧(當然只記錄該段的起始與結束標記);
2。然後繼續對支點的左段重複過程1,若左段的元素小於2個,則不需要再重複1,轉到3;
3。左段已排好,從棧中取出最新的右段,轉到1,若棧空則結束。*/
void quicksort2(int num[],int low,int high)
{
if(low<high)
{
stack st;
int index=partition1(num,low,high);
if(low<index-1)
{
st.push(low);
st.push(index-1);
}
if(index+1<high)
{
st.push(index+1);
st.push(high);
}
while(!st.empty())
{
int p=st.pop();
int q=st.pop();
index=partition1(num,q,p);//注意這裡要注意p,q的順序,因為棧是FIFO的所以先出來的是高位,然後才是低位.所以p對應的是high,q對應的是low
if(q<index-1)
{
st.push(q);
st.push(index-1);
}
if(index+1<p)
{
st.push(index+1);
st.push(p);
}
}
}
}






//歸併排序
void merge(int num[],int start,int middle,int end)
{
int *num1=new int[middle-start+2];//設定兩個新陣列,暫存兩個分出來的子序列
int *num2=new int[end-middle+1];
for(int i=0;i<=middle-start;i++)
num1[i]=num[start+i];
num1[middle-start+1]=100000;//設定標誌位,不設定這個標誌位下面for迴圈的時候i++或者是j++會將會大於middle-start或者是end-middle-1,超過陣列範圍
num2[end-middle]=100000;//設定標誌位,設定標誌位後可以保證陣列不越界
for(int j=0;j<=end-middle-1;j++)
num2[j]=num[middle+j+1];
int i=0,j=0;
for(int k=0;k<=end-start;k++)
{
if(num1[i]<=num2[j])
{
num[start+k]=num1[i];
i++;
}
else
{
num[start+k]=num2[j];
j++;
}


}
delete [] num1;
delete [] num2;
num1=NULL;
num2=NULL;
}
void mergesort(int num[],int start,int end)
{
if(start<end)
{
int middle=(start+end)/2;
mergesort(num,start,middle);
mergesort(num,middle+1,end);
merge(num,start,middle,end);
}
}
//方法2,不設定標誌位,歸併排序
void merge1(int num[],int start,int middle,int end)
{
int *num1=new int[middle-start+1];//設定兩個新陣列,暫存兩個分出來的子序列,不過這種方法浪費了儲存空間,可以改進為下面的方法
int *num2=new int[end-middle];
for(int i=0;i<=middle-start;i++)
num1[i]=num[start+i];
for(int j=0;j<=end-middle-1;j++)
num2[j]=num[middle+j+1];
int i=0,j=0,k=0;   //num2有end-middle個,標號從0開始,故最大j為end-middle-1。一定注意這個標號不能夠設定錯了
for(;k<=end-start&&i<=middle-start&&j<=end-middle-1;k++)//不設定標誌位,則在迴圈的時候就要對i,j進行限制,防止i,j越界
{                                                       //注意這裡i檢測條件設定小於為middle-start+1,因為num1有middle-start+1個,但是從0開始,標號最大middle-start
if(num1[i]<=num2[j])  
{
num[start+k]=num1[i];//注意這裡一定不能夠設定為start++,因為這樣就改變了start大小,在確定num1,num2大小的時候用到了start,造成錯誤
i++;                 //這裡不用start的方法就是,單獨在迴圈前令index=start
}
else
{
num[start+k]=num2[j];
j++;
}
}
while(i<=middle-start)//跳出迴圈肯定是因為i或者是j某一個超過了範圍了
{
num[start+k]=num1[i++];
k++;
}
while(j<=middle-start-1)
{
num[start+k]=num2[j++];
k++;
}
delete [] num1;
delete [] num2;
num1=NULL;
num2=NULL;
}
void mergesort1(int num[],int start,int end)
{
if(start<end)
{
int middle=(start+end)/2;//注意這裡計算出的是middle的位置,也就是中間位置的標號
mergesort1(num,start,middle);
mergesort1(num,middle+1,end);
merge1(num,start,middle,end);
}
}


//遞迴方法實現歸併排序(方法2)
void merge3(int num[],int start,int middle,int end,int*temp)
{
int k=0,i=start,j=middle+1;
for(;i<=middle&&j<=end;)
{
if(num[i]<num[j])
temp[k++]=num[i++];
else
temp[k++]=num[j++];
}
while(i<=middle)
temp[k++]=num[i++];
while(j<=end)
temp[k++]=num[j++];
for(int index=0;index<k;index++)
num[start+index]=temp[index];//注意這裡在跟新num中相應位置排序的時候不能寫為index,一定要寫為start+index
}
void mergesort3(int num[],int start,int end,int* temp)
{
if(start<end)
{
int middle=(start+end)/2;
mergesort3(num,start,middle,temp);
mergesort3(num,middle+1,end,temp);
merge3(num,start,middle,end,temp);
}
}






//非遞迴的方法實現歸併排序
/**
* mergesort: 非遞迴實現 --迭代
* 非遞迴思想: 將陣列中的相鄰元素兩兩配對。用merge函式將他們排序,
* 構成n/2組長度為2的排序好的子陣列段,然後再將他們排序成長度為4的子陣列段,
* 如此繼續下去,直至整個陣列排好序。
*merge_sort(): 非遞迴實現-自底向上
*將原陣列劃分為left[min...max] 和 right[min...max]兩部分
**/
void mergesort2(int arr[],int n)//引數和遞迴略不同,n代表陣列中元素個數,即陣列最大下標是n-1   
{
/* 
int step = 1; 
while(step<n) //當元素個數不是2的冪時可能會出錯,未考慮第2個序列個數不足的情況  

for(int i=0;i<=n-step-1;i+=2*step) 
Merge(arr,i,i+step-1,i+2*step-1); 
step*=2; 
}*/  


int size=1,low,mid,high;  //size為步長,1,2,4,8
while(size<=n-1)  
{  
low=0;  
while(low+size<=n-1)  //先是陣列元素相鄰的兩兩相排序
{  
mid=low+size-1;  
high=mid+size;  //因此有high=low+2*size-1,因此才有最開始方法的i+step-1,i+2*step-1.
if(high>n-1)//針對二路歸併的第二個序列個數不足size個的情況 ,避免了第一個方法的問題,這個問題主要存在於在最後進行歸併的時候的情況,且對於最後一次的歸併的情況,middle不一定是中間位置
high=n-1;         
merge(arr,low,mid,high);//呼叫歸併子函式   
// cout<<"low:"<<low<<" mid:"<<mid<<" high:"<<high<<endl;//打印出每次歸併的區間   
low=high+1;//下一次歸併時第一關序列的下界   
}  
size*=2;//範圍擴大一倍   
}  

//另外一種可行的方法
/*void merge_sort(int a[],int l,int r){  
int len=r-l+1;  
//列舉的是一半的長度   
for(int i=1;i<=len;i*=2){  
int left=l;  
while(left<r){  
int mid=left+i-1;  
int right=left+2*i-1;  
//中間值大於右邊界,說明排好序了   
if(mid>r) break;  
//中間值沒有超,右邊界超了   
if(right>r) right=r;  
//mid和right相等的時候,也不需要排序   
if(right==mid) break;  
merge(a,left,mid,right);  
left=right+1;   
}  
}  
} */




//桶排序
struct bulknode
{
int data;
bulknode *next;
};
void bulksortprint(bulknode *bulk,int bulklength)
{
for(int i=0;i<bulklength;i++)
{
if(bulk[i].next==NULL)
continue;
bulknode *p=bulk[i].next;
while(p!=NULL)
{
cout<<p->data<<" ";
p=p->next;
}
}
cout<<endl;
}
void destroybulk(bulknode *bulk,int bulklength)
{
for(int i=0;i<bulklength;i++)
{
if(bulk[i].next==NULL)
continue;
while(bulk[i].next!=NULL)
{
bulknode *p=bulk[i].next;
bulk[i].next=(bulk[i].next)->next;
delete p;
}
}
}


void bulksort(int num[],int bulklength,int numlength)
{
bulknode *bulk=new bulknode[bulklength];
//清桶,桶頭不放任何元素
for(int i=0;i<bulklength;i++)
{
bulk[i].data=0;//注意這裡要用點操作符,點操作符僅應用與類型別的物件:左運算元必須是類型別的物件,右運算元必須是指定該型別的成員
bulk[i].next=NULL;//因為這裡bulk[i]不是指標只是一個類物件,所以用點操作符
}
for(int i=0;i<numlength;i++)
{
int j=(int)(num[i]/100);
bulknode *p=new bulknode;
p->data=num[i];//箭頭操作符->是點操作符和解引用操作符表示式的同義詞。即(*p).data=num[i]
p->next=NULL;


bulknode *q=&bulk[j];
bulknode *r=bulk[j].next;
while(r!=NULL&&r->data<num[i])
{
q=q->next;
r=r->next;
}
q->next=p;
p->next=r;
}
bulksortprint(bulk,bulklength);
destroybulk(bulk,bulklength);
}


//基數排序
void radixsort(int num[],int length)
{
int max=num[0];
for(int i=1;i<length;i++)
{
if(num[i]>max)
max=num[i];
}
int d=1;//位數
while(max)
{
max/=10;
d++;
}
int *count=new int[10];
for(int i=0;i<10;i++)
count[i]=0;
int *temp=new int[length];
int radix=1;
for(int i=0;i<d;i++)
{
for(int j=0;j<10;j++) //每一輪進行桶排序時,要把計數器中的值清0,否則會出錯,計算出來的不在是某個數的排名。
count[j]=0;
for(int j=0;j<length;j++)
{
int k=(num[j]/radix)%10;//注意這種取到各個位的數值的方法
count[k]++;
}
for(int j=1;j<10;j++)
count[j]=count[j-1]+count[j];//累加之前計算器個數,這樣就可以得到排位,這個類似於計數排序
for(int j=length-1;j>=0;j--)//注意這裡在必須對num從尾到頭遍歷,不能夠從前到後遍歷,即不能j=0;j<length;j++。
{
int s=(num[j]/radix)%10;//從後往前遍歷是因為它是一個穩定的排序,最後出現的值,肯定在排序的時候也是排在相等的值得後面,為了保證不改變它們的出現順序
temp[count[s]-1]=num[j];//注意這裡對於cout[s]必須減去1,個數是從1開始的,而陣列中儲存序號是從0開始的
count[s]--;
}
for(int j=0;j<length;j++)
num[j]=temp[j];
radix=10*radix;//用於計算各個位的值得基準引數
}
delete [] count;
delete [] temp;
}


//計數排序
void countsort(int num[],int length)
{
int max=num[0];
for(int i=1;i<length;i++)
{
if(num[i]>max)
max=num[i];
}
int *count=new int[max+1];//注意是有max+1項,陣列是從0到max的,故有max+1項
for(int i=0;i<=max;i++)
count[i]=0;
for(int i=0;i<length;i++)
count[num[i]]++;  //注意這裡是count[num[i]]++而不是count[num[i]]=1,因為有可能有相等的元素
for(int i=1;i<=max;i++)
count[i]=count[i-1]+count[i];
int *temp=new int[length];
for(int i=length-1;i>=0;i--)//注意這裡一定要是從後往前遍歷,保證不改變元素出現的先後順序
{
temp[count[num[i]]-1]=num[i];
count[num[i]]--;
}
for(int i=0;i<length;i++)
num[i]=temp[i];
delete [] temp;
delete [] count;
}


int main()
{
int length=10;
int num[]={300,421,808,675,200,912,675,999,688,570};
//insertsort(num,length);
//print(num,length);


//shellsort(num,length);
//print(num,length);


//selectsort(num,length);
//print(num,length);


//heapsort(num,length);
//print(num,length);


//bubblesort(num,length);
//print(num,length);
//bubblesort1(num,length);
//print(num,length);


//quicksort(num,length,0,length-1);
//print(num,length);
//quicksort1(num,0,length-1);
//print(num,length);
//quicksort2(num,0,length-1);
//print(num,length);


//mergesort(num,0,length-1);
//print(num,length);
mergesort1(num,0,length-1);
print(num,length);
//mergesort2(num,length);
//print(num,length);


//bulksort(num,10,length);


//radixsort(num,length);
//print(num,length);


//countsort(num,length);
//print(num,length);
return 0;
}