排序算法小結

網上關於排序算法的總結太多了,這篇文章就寫得不錯。

http://m.blog.csdn.net/blog/likaiwalkman/23713373

經典就是經典,個人覺得這些經典的算法被反覆研究幾十遍都不爲過。同時也參考了很經典的書籍《數據結構與算法分析——C語言描述》,溫故而知新,每次回頭看這些算法的時候都爲其中博大精深的思想所折服,呵呵,不扯了。這裏只貼出一份用代碼敲出來的各個排序算法,純粹是想練一下手。主要關注快排和堆排序。


交換排序


快速排序

快排的算法思想很精妙,經常在倆指針問題中用到。

假設待排序的元素個數爲n,分別存放在數組a[1,…,n]中,令第一個元素爲參考元素(稱爲樞紐元,pivot)pivot=a[1],初始時,i=1j=n,按照以下方法操作:

(1)    從第j個元素開始向前依次將每個元素與樞紐元pivot比較。如果當前元素大於等於pivot,則比較前一個元素與pivot,即比較a[j-1]pivot;否則,將當前元素移動到第i個位置,並轉到步驟(2)

(2)    從第i個元素開始向後依次將每個元素與樞紐元pivot比較。如果當前元素小於pivot,則比較後一個元素與pivot,即比較a[i+1]pivot;否則,將當前元素移動到第j個位置,並轉到步驟(3)

(3)    重複執行(1)(2),直到出現i>=j,將元素移動到a[i]中。此時,整個元素序列被劃分成兩個部分,第i個位置之前的元素都小於a[i],第i個位置後的元素都大於等於a[i]。至此,完成一趟快速排序。

按照以上的方法,將每個部分都進行類似的劃分操作,直到每個部分都只有一個元素爲止,這樣整個元素序列就構成了一個有序的序列。

#include<iostream>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
void quicksort(int a[],int start,int end)//對a[start]到a[end]的元素進行排序 
{
	int i=start,j=end;
	int temp;
	if(start<end)
	{
		temp=a[start];
		while(i!=j)
		{
			while(j>i&&a[j]>=temp)
				j--;
			a[i]=a[j];
			while(i<j&&a[i]<=temp)
				i++;
			a[j]=a[i];
		}
		a[i]=temp;
		quicksort(a,start,i-1);
		quicksort(a,i+1,end);
	} 
}
int main()
{
	int a[]={17,1,5,6,98,78,79,54,32,11,15,14,13,20,29};
	int n=sizeof(a)/sizeof(a[0]);
	quicksort(a,0,n-1);
	print(a,n);
	return 0;
}

 

冒泡排序

#include<iostream>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
int main()
{
	int a[]={1,5,78,94,48,34,32,47,55,96,19,5};
	int n=sizeof(a)/sizeof(a[0]);
	int i,j,temp,flag=1;
	for(i=0;i<n&&flag;i++)
	{
		flag=0;
		for(j=0;j<n-i;j++)
		{
			if(a[j]>a[j+1])
			{
				temp=a[j];
				a[j]=a[j+1];
				a[j+1]=temp; 
				flag=1;
			}
		}
		print(a,n);
	}
	return 0;
}


選擇排序

選擇排序的算法思想很簡單,從待排序的元素中選擇最小(最大)的元素放在已經排序的序列的最後,直到全部排完爲止。


堆排序

關於堆排序,C++STL中已經提供了很成熟的函數,像入堆push_heap、創建堆make_heap、出堆pop_heap,判斷是否爲堆is_heap,堆排序sort_heap等,實際應用中可以直接調用。但是,算法的思想還是要掌握的。

假設一個大根堆中有n個元素,如果將堆中的根節點元素輸出後,再將剩下的n-1個元素重新建立成一個新堆,並將根節點元素輸出,然後將剩下的n-2元素重新建立成堆,重複執行以上過程,直到堆中沒有元素爲止。所以堆排序的關鍵就是創建堆調整堆

建立大根堆的算法思想大概過程是這樣的。從位於元素序列中的最後一個一個非葉子結點,即第n/2向下取整個元素開始,逐層比較並調整元素的位置,使其滿足a[i]>=a[2*i]a[i]>=a[2*i+1],直到根節點爲止。假設當前結點的序號爲i,則當前元素爲a[i],其左右孩子結點分別爲a[2*1]a[2*i+1]。將a[2*1]a[2*i+1]較大者與a[i]比較,如果孩子結點元素值大於當前結點值,則交換兩者;否則,不進行交換。逐層向上執行此操作,直到根節點爲止,這樣就建立了一個大根堆。建立小根堆的算法與此類似。

調整堆其實也是重新建立堆的過程。除了堆頂元素外,剩下的元素本身就具有a[i]>=a[2*i]a[i]>=a[2*i+1](i=1,2,3,…,[n/2]向下取整)的性質,也就是說剩下的元素由大到小逐層排列。所以,將剩下的元素調整成大根堆只需要從上往下逐層比較,找出最大的元素將其放在根節點的位置上就可以構成新的大根堆了。

調整堆的大概過程是這樣的。輸出堆頂元素可以將堆頂元素放在堆的最後,也就是將第一個元素與最後一個元素交換,即a[1]a[n]交換一下,那麼,接下來需要調整的元素就是a[1,…,n-1]。從根節點開始,如果其左、右子樹結點元素值大於根節點元素值,選擇較大的一個與根節點進行交換,否則,不交換。逐層重複執行這個操作,直到葉子結點,就完成了堆的調整,構成了一個新的堆。

#include<iostream>
//#include<algorithm>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
void adjustheap(int a[],int low,int high)//調整a[low,high],使其成爲堆
{
	int i=low,j=2*i;//j指向i的左孩子結點 
	int temp=a[i];//根節點暫存臨時變量中
	while(j<=high)
	{
		if(j<high&&a[j]<a[j+1])//右孩子較大
			j++;
		if(temp<a[j])
		{
			a[i]=a[j];
			i=j;//修改i和j的值,以便繼續向下篩選 
			j=2*i;
		} 
		else
			break; 
	}
	a[i]=temp;
}
/*void adjustheap(int a[],int s,int m)
{
	int t,j;
	t=a[s];
	for(j=2*s+1;j<=m;j*=2+1)
	{
		if(j<m&&a[j]<a[j+1])
			j++;
		if(t>a[j])
			break;
		a[s]=a[j];
		s=j;
	}
	a[s]=t;
} */

void heapsort(int a[],int n)
{
	int i,temp;
	for(i=n/2;i>=1;i--)//創建堆 
		adjustheap(a,i,n);
	for(i=n;i>1;i--)
	{
		temp=a[1];
		a[1]=a[i];
		a[i]=temp;
		adjustheap(a,1,i-1);
	}
}

int main()
{
	int a[]={0,11,12,54,47,86,87,98,75,34,70};
	int n=sizeof(a)/sizeof(a[0]);
	//make_heap(a,a+n);
	//sort_heap(a,a+n);
	heapsort(a,n-1);
	print(a,n);
	return 0;
}


 

注:細心的人,會發現上面的程序有個問題,在創建和調整堆的過程中,下標的對應容易出問題,即數據下標是從1開始的。調用C++ STL中的相關函數接口,就沒這個問題。上面的程序是對着教材敲的,沒仔細看這個問題,糾結了好久,呵呵。

這個版本就沒問題。

#include <stdio.h>
void exch(int& a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

void fixdown(int a[], int k, int N)
{
    while(2*k <= N)
    {
        int j=k*2;
        if(j<N && a[j]<a[j+1]) j++;
        if(!(a[k]<a[j])) break;
        exch(a[k], a[j]);
        k = j;
    }
}
void heapsort(int a[], int l, int r)
{
    int k, N=r-l+1;
    int *pq = a+l-1; //notice!
    for(k = N/2; k>=1; k--)
        fixdown(pq, k, N);
    while(N>1)
    {
        exch(pq[1], pq[N]);
        fixdown(pq, 1, --N);
    }
}
int main()
{
    int a[10] = {11,12,54,47,86,87,98,75,34,70};
    heapsort(a, 0, 9);
    for(int i=0; i<10; i++)
        printf("%d ", a[i]);
    printf("\n");
    return 0;
}


 

注:參考了這篇文章

http://www.cnblogs.com/wouldguan/archive/2012/11/12/2766070.html

簡單選擇排序

#include<iostream>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
void selectsort(int a[],int n)
{
	int i,j,k;
	int temp;
	for(i=0;i<n-1;i++)
	{
		k=i;
		for(j=i+1;j<n;j++)
		{
			if(a[j]<a[k])
				k=j;//k記錄最小元素的位置 
		}
		if(k!=i)
		{
			temp=a[i];
			a[i]=a[k];
			a[k]=temp;
		}
	}
}
int main()
{
	int a[]={11,12,54,47,86,89,98,75,34,59};
	int n=sizeof(a)/sizeof(a[0]);
	selectsort(a,n);
	print(a,n); 
	return 0;
}

 

插入排序

直接插入排序

//直接插入排序,屬於穩定的排序算法
#include<iostream>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
int main()
{
	int a[]={17,46,32,15,1,10,6,7,9,18};
	int n=sizeof(a)/sizeof(a[0]);
	int temp,i,j;
	for(i=1;i<n;i++)
	{
		temp=a[i];//取出待排元素放入臨時變量 
		for(j=i-1;j>=0&&temp<a[j];j--)//尋找插入位置 
		{
			a[j+1]=a[j];
		}
		a[j+1]=temp;
		print(a,n);
	}
	return 0;
}


折半插入排序

#include<iostream>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
int main()
{
	int a[]={1,48,46,57,98,96,16,13,18,29,30,45,47};
	int n=sizeof(a)/sizeof(a[0]);
	int temp,i,j,low,high,mid;
	for(i=1;i<n;i++)
	{
		temp=a[i];//待排元素放入臨時變量 
		for(low=0,high=i-1;high>=low;)//尋找插入位置 
		{
			mid=(low+high)/2;
			if(temp<a[mid])
				high=mid-1;
			else
				low=mid+1;
		}
		for(j=i-1;j>=low;j--)//元素移動 
			a[j+1]=a[j];
		a[low]=temp;
		print(a,n);
	}
	return 0;
}


希爾排序

與直接插入排序、折半插入排序相比,希爾排序將待排序的元素劃分爲若干個子序列,在每個組內進行直接插入排序。

#include<iostream>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
int main()
{
	int a[]={1,5,78,94,48,34,32,47,55,96,19,5};
	int n=sizeof(a)/sizeof(a[0]);
	int i,j,inc,temp;
	for(inc=n/2;inc>0;inc/=2)
	{
		for(i=inc;i<n;i++)
		{
			temp=a[i];//取待排元素放入臨時變量 
			for(j=i;j>=inc;j-=inc)//組內進行插入排序,尋找插入位置 
			{
				if(temp<a[j-inc])
				{
					a[j]=a[j-inc];
				}
				else
				    break;
			}
			a[j]=temp;
			print(a,n);
		}
	}
	return 0;
}

 

歸併排序

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
void print(int a[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
void copy(int source[],int dest[],int len,int start)
{
	int i,j=start;
	for(i=0;i<len;i++)
	{
		dest[j]=source[i];
		j++;
	}
}
void merge(int a[],int left,int right)
{
	int begin1,begin2,mid,k=0,len,*b;
	begin1=left;
	mid=(left+right)/2;
	begin2=mid+1;
	len=right-left+1;
	b=(int*)malloc(len*sizeof(int));
	while(begin1<=mid&&begin2<=right)
	{
		if(a[begin1]<=a[begin2])
			b[k++]=a[begin1++];
		else
			b[k++]=a[begin2++];
	}
	while(begin1<=mid)
		b[k++]=a[begin1++];
	while(begin2<=right)
		b[k++]=a[begin2++];
	copy(b,a,len,left);
}
void mergesort(int a[],int left,int right)
{
	int i;
	if(left<right)
	{
		i=(left+right)/2;
		mergesort(a,left,i);
		mergesort(a,i+1,right);
		merge(a,left,right);
	}
}
int main()
{
	int a[]={11,12,54,47,86,89,98,75,34,59};
	int n=sizeof(a)/sizeof(int);
	mergesort(a,0,n-1);
	print(a,n);
	return 0;
}


 

除了以上幾種算法以外,還有基數排序、桶排序、外部排序等等,不想寫了,知道排序的思想就行。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章