九大內部排序算法(快速排序、歸並排序、堆排序、希爾排序、基數排序)


排序(Sorting)是計算機程序設計中的一種重要操作,它的功能是將一個數據元素(或記錄)的任意序列,重新排列成一個按關鍵字有序的序列。


 

文章目錄


由於待排序的記錄數量不同,使得排序過程中涉及的存儲器不同,可將排序方法划分為兩大類:

 

  • 內部排序,是指待排序列完全存放在內存中所進行的排序過程,適合不太大的元素序列。
  • 外部排序,指的是大文件的排序,即待排序的記錄存儲在外存儲器上,待排序的文件無法一次裝入內存,需要在內存和外部存儲器之間進行多次數據交換,以達到排序整個文件的目的。

九大內部排序:直接插入排序、折半插入排序、希爾排序、冒泡排序、快速排序、簡單選擇排序、堆排序、歸並排序以及基數排序。

算法穩定性:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩定的。

注:事實上,基數排序不是基於比較的。


內部排序可分為五大類,插入排序、交換排序、選擇排序、歸並排序和基數排序。導圖關系如下:
這里寫圖片描述


#1.直接插入排序

**直接插入排序(Straight Insertion Sort)**是一種最簡單的排序方法,它的基本操作是將一個記錄插入到已排好序的有序表中,從而得到一個新的、記錄數增1的有序表。

這里寫圖片描述

void InsertSort(char A[], int n)
{
	int i,j;

	for (i = 2; i <= n; i++)
	{
		if (A[i] < A[i-1])
		{
			A[0] = A[i];     //復制為哨兵,A[0]不存放元素
			for (j = i-1; A[0] < A[j]; j--)    //從后往前查待插入位置
			{
				A[j+1] = A[j];
			}
			A[j+1] = A[0];
		}
	}
}

 


#2.折半插入排序

在直接插入排序基礎上,對前面有序部分采用折半查找,減少了比較元素的次數。但有個要求,必須是順序存儲的線性表

這里寫圖片描述

void InsertSort2(char A[], int n)
{
	int i,j,low,high,mid;

	for (i = 2; i <= n; i++)
	{
		A[0] = A[i];     
		low = 1; high = i-1;

		while (low <= high)    //折半查找
		{
			mid = (low+high)/2;

			if (A[mid] > A[0])
				high = mid-1;
			else
				low = mid+1;
		}

		for (j = i-1; j >= high+1; j--)    
		{
			A[j+1] = A[j];
		}

		A[high+1] = A[0];
	}
}

 


#3.希爾排序

**希爾排序(Shell’s Sort)**又稱“縮小增量排序”,它是一種屬於插入排序類的方法,但在時間效率上較前述幾種算法有較大改進。

希爾排序的基本思想是:先將待排序表分割成若干個“特殊”子表,分別進行直接插入排序,當整個表中元素已呈現“基本有序”時,再對全體記錄進行一次直接插入排序。希爾排序的分析是一個復雜的問題,因為它的時間是所取“增量”序列的函數,這涉及一些數學上尚未解決的難題。

注意:希爾排序最后一個增量一定等於1。

這里寫圖片描述

void ShellSort(char A[], int n)
{
	int i,j,dk;

	for (dk = n/2; dk >= 1; dk /= 2)  //dk是步長
	{
		for (i = dk+1; i <= n; ++i)
		{
			if (A[i] < A[i-dk])
			{
				A[0] = A[i];   //A[0]不使用,充當暫存器
				for (j = i-dk; j > 0 && A[0] < A[j]; j-=dk)
				{
					A[j+dk] = A[j];
				}
				A[j+dk] = A[0];
			}
		}
	}
}

 


#4.冒泡排序

冒泡排序(Bubble Sort),是一種計算機科學領域的較簡單的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。

這個算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端,故名。

這里寫圖片描述

void BublleSort(char A[], int n)
{
	int i,j;
	bool flag;

	for (i = 0; i < n-1; i++)
	{
		flag = false;
		for (j = n-1; j > i; j--)
		{
			if (A[j-1] > A[j])
			{
				swap(A[j-1],A[j]);
				flag = true;
			}
		}
		if (!flag)
			return;
	}
}

 


#5.快速排序

**快速排序(Quicksort)**是對冒泡排序的一種改進。快速排序由C. A. R. Hoare在1962年提出。

它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

這里寫圖片描述

//划分——每次划分唯一確定一個元素位置
int Partition(int A[], int low, int high)
{
	int pivot = A[low];  //一般采用嚴蔚敏教材版本,以第一個位置為基准

	while (low < high)
	{
		while (low < high && A[high] >= pivot)
		{
			--high;
		}
		A[low] = A[high];  //將比基准小的元素移動到左端

		while (low < high && A[low] <= pivot)
		{
			++low;
		}
		A[high] = A[low];  //將比基准小的元素移動到右端
	}
	A[low] = pivot;

	return low;
}

//快排——平均時間復雜度O(log2n)
void QuickSort(int A[], int low, int high)
{
	int pivotpos;

	if (low < high)
	{
		pivotpos = Partition(A,low,high);
		//依次對划分后的子表遞歸排序
		QuickSort(A,low,pivotpos-1);
		QuickSort(A,pivotpos+1,high);
	}
}

 


#6.簡單選擇排序

設所排序序列的記錄個數為n。i取1,2,…,n-1,從所有n-i+1個記錄(Ri,Ri+1,…,Rn)中找出排序碼最小的記錄,與第i個記錄交換。執行n-1趟 后就完成了記錄序列的排序。

這里寫圖片描述

void swap(char &a, char &b)
{
	char t = a;
	a = b;
	b = t;
}

void SelectSort(char A[], int n)
{
	int i,j,min;

	for (i = 0; i < n-1; i++)
	{
		min = i;
		for (j = i+1; j < n; j++)
		{
			if (A[j] < A[min])
				min = j;
		}
		if (min != i)
			swap(A[i],A[min]);
	}
}

#7.堆排序

**堆排序(Heapsort)**是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分為大根堆和小根堆,是完全二叉樹。大根堆的要求是每個節點的值都不大於其父節點的值,即A[parent[i]] >= A[i]。在數組的非降序排序中,需要使用的就是大根堆,因為根據大根堆的要求可知,最大的值一定在堆頂。

這里寫圖片描述

//向下調整堆
void AdjustDown(char A[], int k, int len)
{
	int i;
	
	A[0] = A[k];
	for (i = 2*k; i <= len; i*=2)
	{
		if (i < len && A[i] < A[i+1])
			i++;
		if (A[0] >= A[i])
			break;
		else
		{
			A[k] = A[i];
			k = i;
		}
	}
	A[k] = A[0];
}

//創建大頂堆
void BuildMaxHeap(char A[], int len)
{
	int i;

	for (i = len/2; i > 0; i--)  //調整堆
		AdjustDown(A,i,len);
}

//交換兩數
void swap(char &a, char &b)
{
	char t = a;
	a = b;
	b = t;
}
//char A[] = " abfced";
//HeapSort(A,strlen(A)-1);
//堆排序
void HeapSort(char A[], int len)
{
	int i;

	BuildMaxHeap(A,len);  //創建初始堆
	for (i = len; i > 1; i--)  //把最大值頂到堆頂,再與堆底交換
	{
		swap(A[i],A[1]);
		AdjustDown(A,1,i-1);
	}
}

#8.歸並排序

**歸並排序(Merge Sort)**是建立在歸並操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合並,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合並成一個有序表,稱為二路歸並。

這里寫圖片描述

char A[] = "badcgef";
char *B = (char*)malloc((strlen(A)+1)*sizeof(char));
//將A[low...mid]、A[mid+1...high]合並為一個表,合並前A兩子表有序
void Merge(char A[], int low, int mid, int high)
{
	int i,j,k;

	for (k = low; k <= high; k++) //將A全部復制到B中
		B[k] = A[k];
	for (i = low, j = mid+1, k = i; i <= mid && j <= high; k++)
	{
		if (B[i] <= B[j])
			A[k] = B[i++];
		else
			A[k] = B[j++];
	}
	while (i <= mid)   //復制未檢測完表
		A[k++] = B[i++];
	while (j <= high)
		A[k++] = B[j++];
}
//歸並排序
void MergeSort(char A[], int low, int high)
{
	if (low < high)
	{
		int mid = (low + high)/2;
		MergeSort(A,low,mid);
		MergeSort(A,mid+1,high);
		Merge(A,low,mid,high);
	}
}

 


#9.基數排序

**基數排序(Radix Sort)**它不是基於比較進行排序的,而是采用多關鍵字排序思想,借助“分配”“收集”兩種操作對單邏輯關鍵字進行排序。基數排序又分為最高位優先(MSD)排序和最低位優先(LSD)排序。

這里寫圖片描述


#附1:各種內部排序算法性質

這里寫圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM