幾種排序總結(一)

1.交換排序的基本思想是:兩兩比較待排序記錄的關鍵字,若反序即進行交換,直到沒有反序的記錄爲止。

static void swap(int *p,int *s )
{
	int tmp;
	tmp=*p;
	*p=*s;
	*s=tmp;
}

void exchange_sort(int *arr,int len)
{
	for (int i=0;i<len-1;i++)
	{
		for (int j=i+1;j<len;j++)
		{
			if (arr[i]>arr[j])
			{
				swap(&arr[i],&arr[j]);
			}
		}
	}
}

應用交換排序基本思想的主要排序方法有:冒泡排序(Bubble sort)和快速排序(Quick sort)。


</pre><pre name="code" class="cpp" style="color: rgb(85, 85, 85);">//冒泡排序
void bubble_sort(int *arr,int len)
{
	for (int i=0;i<len-1;i++)
	{
		for (int j=0;j<len-i-1;j++)
		{
			if (arr[j]>arr[j+1])
			{
				swap(&arr[j],&arr[j+1]);
			}
		}
	}
}
//改進版冒泡排序,當一趟比較後沒有交換(flag=false)即已經有序,不再進行,直接跳出
void bubble_sort_ex(int *arr,int len)
{
	bool flag=true;
	for (int i=0;i<len-1 && flag;i++)
	{
		flag=false;
		for (int j=0;j<len-i-1;j++)
		{
			if (arr[j]>arr[j+1])
			{
				swap(&arr[j],&arr[j+1]);
				flag=true;
			}
		}
	}
}

快速排序是對冒泡排序的一種改進,基本思想:通過一趟排序將待排記錄分割爲獨立的兩部分,其中一部分記錄關鍵字均比另一部分記錄的關鍵字小,則可對這兩部分記錄繼續進行排序,已達到整個有序。

//快速排序
static int partition(int *arr,int left,int right)
{
	int key=arr[left];
	while (left<right)
	{
		while (left<right && arr[right]>=key)
		{
			right--;
		}
		arr[left]=arr[right];

		while (left<right && arr[left]<=key)
		{
			left++;
		}
		arr[right]=arr[left];
	}

	arr[left]=key;
    return left;
}

static void quick(int *arr,int left,int right)
{
	if (left<right)
	{
		int pivo=partition(arr,left,right);
		quick(arr,left,pivo-1);
		quick(arr,pivo+1,right);
	}
}

void quick_sort(int *arr,int len)
{
	quick(arr,0,len-1);
}


2.選擇排序的基本思想是:每一趟在n-i-1個記錄中選取關鍵字最小的記錄作爲有序序列中第i個記錄。

最簡單的是簡單選擇排序,一趟簡單的選擇排序的操作爲:通過n-i次關鍵字間的比較,從n-i-1個記錄中選擇出關鍵字最小的記錄,並和第i個記錄交換。

通俗地講就是,對比數組中前一個元素跟後一個元素的大小,如果後面的元素比前面的元素小則用一個變量k來記住他的位置,接着第二次比較,前面"後一個元素"現變成了"前一個元素",繼續跟他的"後一個元素"進行比較如果後面的元素比他要小則用變量k記住它在數組中的位置(下標),等到循環結束的時候,我們應該找到了最小的那個數的下標了,然後進行判斷,如果這個元素的下標不是第一個元素的下標,就讓第一個元素跟他交換一下值,這樣就找到整個數組中最小的數了。然後找到數組中第二小的數,讓他跟數組中第二個元素交換一下值,以此類推。

//選擇排序
void select_sort(int *arr,int len)
{
	int min=arr[0];
	int min_index=0;
	int i;
	int j;
	for (i=0;i<len-1;i++)
	{
		min=arr[i];
		min_index=i;

		for (j=i+1;j<len;j++)
		{
			if (min>arr[j])
			{
				min=arr[j];
				min_index=j;
			}
		}

		if(i!=min_index)//下標並沒有改變
		{
			swap(&arr[i],&arr[min_index]);
		}
	}
}
選擇排序的主要操作是進行關鍵字間的比較,因此,改進簡單選擇排序應從減少“比較”出發。基於選擇排序的思想,出現了堆排序

堆是一個近似完全二叉樹的結構,並同時滿足堆性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。

爲使記錄序列按關鍵詞非遞減有序排列,則在堆排序的算法中先建一個“大根堆:,即先選得一個關鍵字爲最大的記錄並與序列最後一個記錄交換,然後對序列中前n-1個記錄進行篩選,重新調整爲大根堆,如此反覆直至排序結束。

//堆排序
static void heap_adjust(int *arr,int start,int len)
{
	int tmp=arr[start];
	int i;
	while ((i=2*start+1)<len)
	{
		if (i+1<len && arr[i]<arr[i+1])
		{
			i++;
		}

		if (tmp>=arr[i])
		{
			break;
		}
		arr[start]=arr[i];
		start=i;
	}

	arr[start]=tmp;
}

void heap_sort(int *arr,int len)
{
	for (int start=len/2-1;start>=0;start--)
	{
		heap_adjust(arr,start,len);
	}

	for (int i=len;i>1;i--)
	{
		swap(&arr[0],&arr[i-1]);
		heap_adjust(arr,0,i-1);
	}

}


3.插入排序的基本思想是:每步將一個待排序的紀錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到全部插入完爲止。
//插入排序
//void insert_sort(int *arr,int len)
//{
//	int tmp;
//	int j;
//
//	for (int i=1;i<len;i++)
//	{
//		tmp=arr[i];
//		for (j=0;j<i;j++)
//		{
//			if (arr[j]>=tmp)
//			{
//				break;
//			}
//		}
//
//		for (int k=i-1;k>=j;k--)
//		{
//			arr[k+1]=arr[k];
//		}
//
//		arr[j]=tmp;
//	}
//}

void insert_sort(int *arr,int len)
{
	int tmp;
	int j;

	for (int i=1;i<len;i++)
	{
		tmp=arr[i];
		for (j=i-1;j>=0;j--)
		{
			if (arr[j]<=tmp)
			{
				break;
			}
			arr[j+1]=arr[j];
		}

		arr[j+1]=tmp;
	}
}
註釋掉的代碼符合人們的思維邏輯,但效率呢?從註釋掉的代碼到未註釋的代碼,想必你已經看出哪些地方進行了改進,未註釋的代碼纔是真正的直接插入排序。當然還有其他的插入排序,這裏再展示一種:

void half_insert_sort(int *arr,int len)
{
	int tmp;
	int j;
	int left;
	int right;
	int mid;

	for (int i=1;i<len;i++)
	{
		tmp=arr[i];
		left=0;
		right=i-1;

		while (left<=right)
		{
			//mid=(left+right)/2;
			mid=(right-left+1)/2+left;
			if (tmp<arr[mid])
			{
				right=mid-1;
			}
			else
			{
				left=mid+1;
			}
		}

		for (j=i-1;j>=right+1;j--)
		{
			arr[j+1]=arr[j];
		}

		arr[j+1]=tmp;
	}
}
希爾排序又稱“縮小增量排序”,是基於插入排序的思想,但在時間效率上較前幾種排序有較大改進。它的基本思想是:先將整個待排記錄序列分割成若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。

//希爾排序
static void shell(int *arr,int len,int gap)
{
	int tmp;
	int i;
	int j;

	for (i=gap;i<len;i++)
	{
		tmp=arr[i];
		for (j=i-gap;j>=0;j-=gap)
		{
			if (arr[j]<tmp)
			{
				break;
			}
			arr[j+gap]=arr[j];
		}

		arr[j+gap]=tmp;
	}
}

void shell_sort(int *arr,int len)
{
	//for (int gap=len/2;gap>0;gap/=2)
	//{
	//	shell(arr,len,gap);
	//}

	int gap_index[]={701,301,132,57,23,10,4,1};//特殊序列
	int gap_len=sizeof(gap_index)/sizeof(gap_index[0]);

	int i;
	for (i=0;i<len;i++)
	{
		if (gap_index[i]<len)
		{
			break;
		}
		i++;	
	}

	for (i;i<gap_len;i++)
	{
		shell(arr,len,gap_index[i]);
	}
}
那麼,分幾組對希爾排序的效率有沒有影響呢?答案是肯定的,關於如何選擇增量,希爾排序效率更高,可以作爲思考。可在維基百科上查看有關希爾排序怎樣分組。


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

//歸併排序

static void merge(int *arr,int len,int gap)
{
	int *buff=(int *)malloc(sizeof(int)*len);
	assert(buff!=NULL);
	int k=0;
	
	int low1=0;
	int high1=low1+gap-1;
	int low2=high1+1;
	int high2=low2+gap-1<len ? low2+gap-1:len-1;//如果越界,進行拉回

	while (low2<len)//歸併段2只要有,就需要繼續歸併
	{
		while (low1<=high1 && low2<=high2)
		{
			if (arr[low1]<=arr[low2])
			{
				buff[k++]=arr[low1++];
			} 
			else
			{
				buff[k++]=arr[low2++];
			}
		}	
		while (low1<=high1)
		{
			buff[k++]=arr[low1++];
		}
		while (low2<=high2)
		{
			buff[k++]=arr[low2++];
		}

		low1=high2+1;
		high1=low1+gap-1;
		low2=high1+1;
		high2=low2+gap-1<len ? low2+gap-1:len-1;//如果越界,進行拉回
	}

	while (low1<len)//沒有歸併段2,但有歸併段1  
	{
		buff[k++]=arr[low1++];
	}

	for (int i=0;i<len;i++)
	{
		arr[i]=buff[i];
	}

	free(buff);
}

static void merge_ex(int *arr,int len,int gap)
{
	int *buff=(int *)malloc(sizeof(int)*gap*2);
	assert(buff!=NULL);
	int *p=buff;
	int m=0;

	int low1=0;
	int high1=low1+gap-1;
	int low2=high1+1;
	int high2=low2+gap-1<len ? low2+gap-1:len-1;

	while (low1<len)
	{
		if (low2<len)
		{
			while (low1<=high1 && low2<=high2)
			{
				if (arr[low1]<=arr[low2])
				{
					*buff=arr[low1++];
					buff++;
				} 
				else
				{
					*buff=arr[low2++];
					buff++;
				}
			}	
		}
		while (low1<=high1)
		{
			*buff=arr[low1++];
			buff++;
		}
		while (low2<=high2)
		{
			*buff=arr[low2++];
			buff++;
		}

		buff=p;
		for (int i=0;i<2*gap && m<len;i++)
		{
			arr[m]=*buff;
			buff++;
			m++;
		}

		buff=p;
		low1=high2+1;
		high1=low1+gap-1<len ? low1+gap-1:len-1;
		low2=high1+1;
		high2=low2+gap-1<len ? low2+gap-1:len-1;
	}
	free(buff);
}


void merge_sort(int *arr,int len)
{
	for (int gap=1;gap<len;gap*=2)
	{
		//merge(arr,len,gap);
		merge_ex(arr,len,gap);
		//show(arr,len);
	}
}





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