数据结构-排序专题(选择、插入、希尔、冒泡、堆、快速、归并、计数)

选择排序

⚠️工作原理

第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。

🐯代码实现

这里我以实现升序为例。

void SelectSort(int* a, int n)
 85	{
 86		int left = 0;
 87		int right = n - 1;
 88	
 89		while (left < right)
 90		{
 //假设最大的最小的元素都是最左边的那个,
 //循环找出真正的最大值和最小值。
 91			int maxi =left;
 92			int mini = left;
 93			for (int i = left + 1; i <= right; i++)
 94			{
 95				if (a[i] > a[maxi])
 96					maxi = i;
 97				if (a[i] < a[mini])
 98					mini = i;
 99			}
			 //找出最小值后放到最左边
 100			Swap(&a[left], &a[mini]);
 101		//此处是坐标left和maxi相等
            //如果最大值在最左边,把最小值放在左边后,
            //这时最大值已经被覆盖了,需要修正。
 102			if (left == maxi)
 103				maxi = mini;
             //把最大值放在最右边
 104			Swap(&a[right], &a[maxi]);
 105			left++;
 106			right--;
 107		}
 108	}
	

插入排序

⚠️工作原理

插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。

🐯代码实现

这里我以实现升序为例

void InsertSort(int* a, int n)
 27	{
 28		for (int i = 0; i < n-1; i++)
 29			{
 30				int end = i;
 31				int tmp = a[end + 1];
 32		//单趟排序,[0,end]有序,
        //在a【end+1】处先放插入的新值,
 33				while (end >= 0)
 34				{
 35					if (a[end] > tmp)
 36					{
        //如果新插入的值小,就让他和前面的数比较,
        //让前面的数往后移
 37						a[end + 1] = a[end];
 38						end--;
 39					}
 40					else
 41					{
 42						break;
 43					}
 44						
 45				}
 46				a[end + 1] = tmp;
 47			}
 48	
 49	}	

希尔排序

⚠️工作原理

希尔排序是把记录按下标的一定增量(代码中用gap代替)分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。

🐯代码实现

void ShellSort(int* a, int n);
 51	//一个数组【2,7,53,2,1,8】
 52	//分组,在每一组内部比较大小
 53	void ShellSort(int* a, int n)
 54	{
        //gap就是增量
 55		int gap = n;
 56		while(gap>1)
 57		{
       //大量的实验表明,gap=3是不错的选择,
       //此处gap每次除3,让增量变小,可以让结果更加精确,
       //为了让每个数字都被分到组中,➕1就能解决这个问题
 58			gap = gap / 3 + 1;
 59			//i的范围是n-gap
 60			for (int i = 0; i < n - gap; i++)
 61			{
 62				int end = i;
 63				int tmp = a[end + gap];
 64				while (end >= 0)
 65				{
 66					if (a[end] > tmp)
 67					{
 68						a[end + gap] =a[end];
 69						end -= gap;
 70					}
 71					else
 72						break;
 75				}
 76				a[end + gap] = tmp;
 77			}
 78		}
    }
  

冒泡排序

⚠️工作原理

重复走访所有的元素,依次比较两个相邻的元素,如果顺序错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,排序完成。

🐯代码实现

void BubbleSort(int* a, int n)
 152	{
        //外循环,循环的次数是元素个数(n-1)次
        //助记:假设有5个元素,第一次排序把最大的数沉到最后,
        //                  第二次排序把次大的数沉到最后
        //                 .....
        //                  第四次排序把次小的沉到第二个位置
        //最小的那个数就已经在第一位了
        
 153		for (int j = 0; j < n-1; j++)
 154		{
 155			int i = 0;
 156			int flag = 0;
 157			for (i = 0; i < n-j; i++)
 158			{
 159				if (a[i] > a[i + 1])
 160				{
 161					Swap(&a[i], &a[i + 1]);
 162					flag = 1;
 163				}
 164				
 165			}
 166			if (flag == 0)
 167			{
 168				break;
 169			}
 170		}
 171	}

堆排序

⚠️工作原理

堆排序运用了堆的性质,所有根节点大于(小于)他的子节点,要实现堆排序,只需要把原来的无序的二叉树调整成堆即可。

三步走实现堆:
1,把根节点的左右子树都调整成小堆(用向下调整算法)
2,将堆中的所有数据重新排序(从根节点开始向下调整)
3,移除位在第一个数据的根节点,再调整。

🐯代码实现

子函数(向下调整算法)

void AdjustDown(int* a, int n, int root)
 113	{
 114		int parent = root;
 115		int child = parent * 2 + 1;
 116		while (child < n )
 117		{
 118			if (a[child + 1] > a[child]&&(child+1<n))
 119				child++;
 120			if (a[parent] < a[child])
 121			{
 122				Swap(&a[parent], &a[child]);
 123				parent = child;
 124				child = parent * 2 + 1;
 125			}
 126			else
 127			{
 128				break;
 129			}
 130			
 131		}
 132	}
void HeapSort(int* a, int n)
 135	{
          //从最后一个节点的父节点开始调整
 136		for (int i = (n - 1 - 1 / 2); i >= 0; i--)
 137		{
 138			AdjustDown(a, n, i);
 139		}
 140		size_t end = n - 1;
 141		while (end>0)
 142		{
 143			Swap(&a[0], &a[end]);
 144			AdjustDown(a, end, 0);
 145			end--;
 146		}
 147	
 148	}

快速排序

⚠️hoare(左右指针法)

🌲思路

1,选出一个key,一般是最左边或者最右边
2,定义两个指针left,right,left向右走,right向左走
(需要注意的是,如果key是最左边,那么right先走;如果key是最右边,left先走)
3,(选最右边的数是key)left先走,在走的过程中,如果遇到比key大的数,就停下来,right再走,right遇到比key小的数就停下来,然后交换left和right的值,交换后两个指针继续走,直到left和right相遇。最后将相遇点的数和key交换。

🐯代码实现

//左右指针法(hoare)
 187	int partSort1(int* a,int left, int right)
 188	{
 189		//假设key是最右边的值
 190		int keyindex = right;
 191		while (left < right)
 192		{
 193			while(a[left] < a[keyindex]&&(left<right))
 194			{
 195					left++;
 196			}
 197			while (a[right] > a[keyindex]&&(left<right))
 198			{	
 199				right--;
 200			}
 201			Swap(&a[left], &a[right]);
 202		}
 203		Swap(&a[left], &a[keyindex]);
 204	
 205		return left;
 206	}
 207	

⚠️挖坑法

🌲思路

1,选出一个key,一般是最左边或者最右边,此时key的位置就形成了一个坑。
2,定义两个指针left,right,left向右走,right向左走
(需要注意的是,如果key是最左边,那么right先走;如果key是最右边,left先走)
3,(选最右边的数是key)left先走,在走的过程中,如果遇到比key大的数,就把这个数放到坑里,这个数的位置就是新的坑。right再走,right遇到比key小的数就停下来,把这个数放到坑里,这个位置再次形成新的坑。直到left和right相遇。最后将相遇点赋值为key。

🐯代码实现

//挖坑法
 209	int partSort2(int* a, int left, int right)
 210	{
 211		int key =a[ right];
            //pit就是坑
 212		int pit = right;
 213		while (left < right)
 214		{
 215			while (a[left] < key && left < right)
 216				left++;
 217			a[pit] = a[left];
 218			pit = left;
 219	
 220			while (a[right] > key && left < right)
 221				right--;
 222			a[pit] = a[right];
 223			pit = right;
 224		}
 225		a[pit] = key;
 226		return pit;
 227	
 228	}
 229	

前后指针法

🌲思路

1,选出一个key,一般是最左边或者最右边
2,定义一个指针cur从第一个元素开始走,另一个指针prev指向cur的前一个元素。
3,(选最右边的数是key)cur遇到比key大的停下,prev++,然后交换prev和cur的值,然后cur继续走,直到cur走到倒数第二个数,循环停止,prev++,交换prev和key。

🐯代码实现

//前后指针法
 231	int partSort3(int* a, int left, int right)
 232	{
 233		int keyi = right;
 234		int prev = left-1;
 235		int cur = left ;
 236		while (cur < right)
 237		{
 238			if (a[cur] < a[keyi] && a[prev++] != a[cur])
 239			{
 240				Swap(&a[prev], &a[cur]);
 241			}
 242			cur++;
 243		}
 244		Swap(&a[prev], &a[keyi]);
 245		return prev;
 246	}

归并排序

⚠️工作原理

把原来无序的数组分成几个子序,再把子序分成更小的子序,直到子序不能再分,然后给子序排序,直到原数组有序。

🐯 代码实现

子函数

void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
     //把一个区间分成两个小的区间
	int mid = (begin + end) / 2;
	// 新的小区间 [begin, mid][mid+1, end]
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid+1, end, tmp);

	// 归并[begin, mid][mid+1, end]
	//printf("归并[%d,%d][%d,%d]\n", begin, mid, mid+1, end);
	int begin1 = begin, end1 = mid;
	int begin2 = mid+1, end2 = end;
	int index = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
			tmp[index++] = a[begin1++];
		else
			tmp[index++] = a[begin2++];
	}

	while (begin1 <= end1)
		tmp[index++] = a[begin1++];

	while (begin2 <= end2)
		tmp[index++] = a[begin2++];

	memcpy(a+begin, tmp+begin, (end - begin + 1)*sizeof(int));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	assert(tmp);

	_MergeSort(a, 0, n - 1, tmp);

	free(tmp);
}

计数排序

⚠️工作原理

遍历数组,找出最小,最大的数,作为区间的头和尾,然后把数组的每个数对应放到这个区间里,放好之后,再一一读取出来放到原数组。

🐯代码实现

注意数要放到区间的相对位置上。

void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 1; i < n; ++i)
	{
		if (a[i] < min)
			min = a[i];

		if (a[i] > max)
			max = a[i];
	}

	int range = max - min + 1;
	int* countA = (int*)malloc(sizeof(int)*range);
	assert(countA);
	memset(countA, 0, sizeof(int)*range);

	// 计数
	for (int i = 0; i < n; ++i)
	{
		countA[a[i] - min]++;
	}

	// 排序
	int j = 0;
	for (int i = 0; i < range; ++i)
	{
		while (countA[i]--)
		{
			a[j++] = i + min;
		}
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值