各種排序算法

在C++和數據結構的學習中,我學到了很多種排序方法。

下面我簡單列出這些排序算法的C++實現方法。吐舌頭還有很多不成熟的地方,以後會及時改正!!!

#include<stack>
#include<assert.h>

//直接插入排序
void InsertSort(int* a,size_t size)
{
	assert(a);
	for (int i = 0; i < size - 1; ++i)
	{
		//進入for循環,tmp保存end後一個位置的值
		int end = i;
		int tmp = a[end + 1];
		//end之前的值所有比tmp大的都向後挪
		while (end>=0 && a[end]>tmp)
		{
			//將end位置的值向後挪一個位置    
			a[end + 1] = a[end];
			--end;
		}   
		a[end + 1] = tmp;  
	}
}

//打印數組
void PrintArray(int* a,size_t size)
{
	for (int i = 0; i < size; ++i)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

//希爾排序
//原理類似於插入排序,把數組分爲不同組,每兩個元素之間有一定的間隔,提高了效率
void ShellSort(int* a,size_t size)
{
	assert(a);
	int gap = size;
	while (gap > 1)
	{
		gap = gap/3+1;  //元素間隔
		for (int i = 0; i < size-gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0 && a[end] > tmp)
			{
				a[end + gap] = a[end];
				end -= gap;
			}
			a[end + gap] = tmp;
		}
	}
}

//堆排序
void AdjustDown(int* a, size_t size, size_t parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		//child指向左子樹,若存在右子樹且大於左子樹,則child指向右子樹
		if ((child+1<size) && (a[child]<a[child+1]))
		{
			++child;
		}
		//孩子節點大於父節點,就交換,同時更新父節點和子節點
		if (a[child] > a[parent])
		{
			swap(a[parent],a[child] );
			parent = child;
			child = parent * 2 + 1;
		}
		//已經爲有序狀態
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, size_t size)
{
	assert(a);
	//建堆(大堆)
	for (int i=(size-2)/2; i >= 0; --i)
	{
		AdjustDown(a, size, i);
	}
	//調整
	for (int i = 0; i < size-i-1; ++i)
	{
		swap(a[0], a[size-i-1]);
		AdjustDown(a, size-i-1, 0);
	}
}


//單趟排序
int PartSort1(int* a, int left, int right)
{
	//以最後一個值爲key
	int key = a[right];
	int begin = left;
	int end = right;
	while (begin < end)
	{
		//begin從前往後找到大與key的值,則停止
		while (begin<end && a[begin]<key)
		{
			++begin;
		}
		//end從後往前找到小於key的值,則停止
		while (begin<end && a[end]>key)
		{
			--end;
		}
		//將大於key的值換到後面,將小於key的值換到前面
		if (begin < end)
		{
			swap(a[begin], a[end]);
		}
	}
	//解決只有兩個元素時出現的bug
	if (a[begin]>a[right])
	{
		swap(a[begin], a[right]);
		return begin;
	}
	else
	{
		return right;
	}
}

//挖坑法
int PartSort2(int* a, int left, int right)
{
	int key = a[right];
	while (left < right)
	{
		//找到left的值大於key,停止,否則向後走
		while (left < right && a[left] <= key)
		{
			++left;
		}
		//此時將left的值填到剛纔挖走的right上
		if (left < right)
		{
			a[right] = a[left];
		}
		//找到right的值小於key,停止,否則向前走
		while (left < right && a[right] >= key)
		{
			--right;
		}
		//此時將right的值填到剛纔挖走的left上
		if (left < right)
		{
			a[left] = a[right];
		}
	}
	a[left] = key;
	return left;
}

//三數取中間
int GetMidIndex(int* a, int left, int right)
{
	int mid = left + (right - left) / 2;
	if (a[mid] < a[right])
	{
		//a[left]  a[mid]  a[right]
		if (a[left] < a[mid])
		{
			return mid;
		}
		//a[mid]  a[right]  a[left]
		if (a[right] < a[left])
		{
			return right;
		}
		//a[mid]  a[left]  a[right]
		else
		{
			return left;
		}
	}
	else
	{
		//a[right]  a[mid]  a[left]
		if (a[mid] < a[left])
		{
			return mid;
		}
		//a[left]  a[right]  a[mid] 
		if (a[left] < a[right])
		{
			return right;
		}
		//a[right]  a[left]  a[mid]
		else
		{
			return left;
		}
	}
}

//優化---解決快排基本有序時效率太低問題
int PartSort3(int* a, int left, int right)
{
	int midIndex = GetMidIndex(a, left, right);//得到中間值
	int key = a[right];
	int prev = left - 1;
	int cur = left;
	while (cur < right)
	{
		//cur探路,找小       2, 5, 7, 1, 4, 8, 0, 9, 6, 3
		//prev找大            2, 1, 0, 3, 4, 8, 7, 9, 6, 5
		if (a[cur] < key && ++prev != cur)
		{
			swap(a[cur], a[prev]);
		}
		++cur;
	}
    //將key放在剛纔prev的下一個位置
	swap(a[++prev], a[right]);
	return prev;
}



//快速排序
void QuickSort(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
	{
		return;
	}
	//數組中元素個數小於13,用插入排序效率更高
	if (left - right < 13)
	{
		InsertSort(a, right-left+1);
	}
	//快排遞歸
	else
	{
		int div = PartSort3(a, left, right);
		//遞歸排序,解決每一個子問題
		QuickSort(a, left, div - 1);
		QuickSort(a, div + 1, right);
	}
}

//選擇排序
void SelectSort(int* a, int size)
{
	int left = 0;
	int right = size - 1;
	while (left < right)
	{
		//[left,right]  閉區間
		for (int i = left; i <= right; ++i)
		{
			if (a[i] < a[left])
			{
				swap(a[i], a[left]);
			}
			if (a[i]>a[right])
			{
				swap(a[i], a[right]);
			}
		}
		//區間逐漸向中間收攏,直到left和right相遇
		++left;
		--right;
	}
}

//非遞歸快速排序
void QuickSort_NonR(int* a, int left, int right)
{
	assert(a);
	stack<int> s; 
	if (left < right)
	{
		//得到中間點
		int mid = PartSort3(a, left, right);
		//左半部分
		if (left < mid - 1)
		{
			s.push(left);
			s.push(mid - 1);
		}
		//右半部分
		if (mid + 1 < right)
		{
			s.push(mid + 1);
			s.push(right);
		}
		while (!s.empty())
		{
			//每部分先push的是左,後push的右
			//取時先得到每部分的右,再是左
			int r = s.top();
			s.pop();   //棧中取出數,需要pop一下
			int l = s.top();
			s.pop();
			//給當前的小數組單趟排序,並且得到中間點
			mid = PartSort3(a, l, r);
			if (l < mid - 1)
			{
				s.push(l);
				s.push(mid - 1);
			}
			if (mid + 1 < r)
			{
				s.push(mid + 1);
				s.push(r);
			}
		}
	}
}

//合併每兩個排好序的小組元素
void _Merge(int* a, int* tmp, int begin, int mid, int end)
{
	int index = begin;
	int i = begin;
	int j = mid + 1;
	while (i <= mid && j <= end)
	{
		//先排小的
		if (a[i] < a[j])
		{
			tmp[index++] = a[i++];
		}
		else
		{
			tmp[index++] = a[j++];
		}
	}
	//剩餘數據
	while (i <= mid)
	{
		tmp[index++] = a[i++];
	}
	while (j <= end)
	{
		tmp[index++] = a[j++];
	}
	//將tmp中數據複製到a中
	for (int i = 0; i < index; i++)
	{
		a[i] = tmp[i];
	}
}

//遞歸合併
void _MergeSort(int* a, int* tmp, int left, int right)
{
	if (a == NULL || left >= right)
	{
		return;
	}
	int mid = left + (right - left) / 2;
	//每次遞歸進行找數組的左半部分
	_MergeSort(a, tmp, left, mid);
	//每次遞歸進行找數組的右半部分
	_MergeSort(a, tmp, mid + 1, right);
	//將得到的左右兩部分進行合併
	_Merge(a, tmp, left, mid, right);
}

//歸併排序
void MergeSort(int* a, size_t size)
{
	assert(a);
	//新建一塊size大小的空間
	int* tmp = new int[size];
	_MergeSort(a, tmp, 0, size - 1);
	delete[] tmp;
}

//計數排序
void CountSort(int* a, size_t size)
{
	assert(a);
	int min = a[0];
	int max = a[0];
	for (size_t i = 0; i < size; ++i)
	{
		//找到最小值
		if (a[i] < min)
		{
			min = a[i];
		}
		//找到最大值
		if (a[i] > max)
		{
			max = a[i];
		}
	}
	int range = max - min + 1;  //計數範圍
	int* count = new int[range];//計數區間
	memset(count, 0, sizeof(int)*range);
	for (size_t i = 0; i < size; ++i)
	{
		//統計每個元素出現的次數,放在對應位置上
		//已有順序
		count[a[i] - min]++;
	}
	size_t index = 0;
	for (int i = 0; i < range; ++i)
	{
		//可能有重複數據
		while (count[i]--)
		{
			//此時在range範圍中,下標即爲需存儲的值
			a[index++] = i + min;
		}
	}
}

//獲取最大基數
int GetMaxRadix(int* a, size_t size)
{
	int radix = 1;
	int max = 10;
	for (size_t i = 0; i < size; ++i)
	{
		while (a[i] > max)
		{
			max *= 10;
			++radix;
		}
	}
	return radix;
}

//基數排序
void LSDSort(int* a, size_t size)
{
	assert(a);
	int maxRadix = GetMaxRadix(a, size);
	int count[10] = { 0 };//計數器
	int start[10] = { 0 };//收集器
	int* bucket = new int[size];//創建size個桶
	int radix = 1;//基數至少爲一
	for (int i = 1; i <= maxRadix; ++i)//進行maxRadix次排序
	{
		memset(count, 0, sizeof(int)* 10);//初始化計數器
		for (size_t i = 0; i < size; ++i)
		{
			int num = (a[i] / radix) % 10;//計算數據在計數器中的位置
			count[num]++;//統計每個桶中的數據個數
		}
		size_t index = 1;
		start[0] = 0;
		while (index < 10)
		{
			start[index] = start[index - 1] + count[index - 1];//收集
			++index;
		}
		for (size_t i = 0; i < size; ++i)
		{
			int num = (a[i] / radix) % 10;
			bucket[start[num]++] = a[i];//按照start中位置信息將數組數據依次存入桶中
		}
		radix *= 10;
		memcpy(a, bucket, sizeof(int)*size);//將桶中內容拷貝到a中
	}
	delete[] bucket;
}



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