各种排序算法

在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;
}



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