数据结构中常见的几种排序算法

 

 

数据结构中常见的几种排序算法

快速排序

目录

数据结构中常见的几种排序算法

快速排序

插入排序

希尔排序

归并排序

堆排序

选择排序

冒泡排序

基数排序

快速排序

基本思想: 基准分割法

 a) 通过一趟排序将要排序的数据分割成独立的两部分其中一部分所有的数据要比另外的一部分数据都要小

 b) 按照此方法对这两部分数据进行快速排序

 案例:3 6 5 8 4 2 1 9

   快排过程:3 6 5 8 4 2 1 9 => 2 1 |3| 6 5 8 4 9 => 1 |2| |3| 5 4 |6| 8 9 => 1 |2| |3| 4 |5| |6| |8| 9 => 1 2 3 4 5 6 8 9 

   改进的快排:随机快排

  思想:若对数组9 8 7 6 5 4 3 2 1进行快排,每次选取第一个元素作为基准元素,分组将很不均衡,这种极端情况将 导致时间复杂度和简单排序一样。为避免这样的极端情况,选取一个随机数作为基准元素

步骤:

a) 从数列中选一个元素 称“基准”

b) 重新排列数列,所有元素比基准小的摆放在基准的前面,所有比基准大的元 “ 素摆放在基准后面,这个分区退出之后 基准就处于数列的中间位置,称为分区操作,

c) 递归的把小于基准值元素的子数列和大于基准值元素的子数列排序

 

《数据结构中常见的几种排序算法》

//快速排序
int QuickSort(int a[], int left,int right)
{
	int i = left, j = right, x = a[left];
	while (i < j)
	{
		while (i<j && a[j]>x)
			j--;
		if (i < j)
			a[i++] = a[j];


	
		while (i < j && a[i] < x)
			i++;
		if (i < j)
			a[j--] = a[i];
	a[i] = x;
	QuickSort(a, left, right - 1);
	QuickSort(a, left + 1, right);
	}
return 0;
}

插入排序

基本原理 :

构建有序序列,对于未排序数据 在已经排序序列中从后往前扫描,找到相应的位置进行插入,在从后向前的扫描过程中需要反复把已经排序的元素逐步向后挪位,为最新元素插入提供空间。

步骤:

a 从第一个元素开始,该元素可以认为已经被排序

b 取出下一个元素 在已经排序的元素序列中从后向前扫描

c 如果该元素(已排序)大于新元素,将该元素移到下一位置

d 重复c步骤直到找到已排序的元素小于或者等于新元素的位置

e 将新元素插入到该位置

f 重复步骤b

//插入排序
void InsertSort(int a[], int size)
{
	for (int i = 0; i < size; i++)
	{
		int get = a[i];
		int j = i - 1;
		while (j>=0 && a[j]>get)
		{
			a[j + 1] = a[j];
			j--;
		}
		a[j + 1] = get;
	}
}

希尔排序

增量排序,又叫递减增量排序,也是直接插入排序算法的一种高效的改进版本 是不稳定的

基本思想 :

将元素进行同余分组,比如元素个数有8个,若将其分为d1=4组,即每一个元素的下标进行模3运算,下标{0,4}模4余数都为0为一组,{1,5}余1 为一组,{2,6}余2为一组,{3,7}余3为一组,当然这只是一种逻辑上的划分,并不是物理上对其进行切分。然后在各组内进行直接插入排序,排序完再对其进行分组,一般取d(i+1) = ⌊d(i)/2⌋,此时的话d2=⌊d1/2⌋= 2组,就这样一直分组排序到di = 1并插入排序结束

 

 

《数据结构中常见的几种排序算法》

《数据结构中常见的几种排序算法》

//希尔排序  高效的插入排序
void ShellSort(int a[], int size)
{
	int h = 0;
	while (h <= size)
	{
		h = h * 3 + 1;
	}

	while (h >= 1)
	{	
		for (int i = h; i < size; i++)
		{
			int j = i - h;
			int get = a[i];
			while (j >= 0 && a[j]>get)
			{
				a[j + h] = a[j];
				j = j - h;
			}
			a[j + h] = get;
		}
		h = (h - 1) / 3;
	}
}

归并排序

采用分治法()

基本思想:

1)将已经有序的子序列合并,得到完全有序的序列,

2)先使每个子序列有序,再使子序列段间有序

3)若将两个有序表合并成一个有序表称为二路归并

 

步骤:

a 申请空间,让他大小为两个已经排序的序列之和,

该空间用来存放合并后的序列

b 设定两个指针,最初位置分别为两个已经排序序列起始位置

c 比较两个指针所指向的元素,选择相对较小的放入合并空间,并移动

指针到下一个位置

d 重复步骤c 知道某个指针达到序列尾

e 将另一个序列剩下的所有元素直接复制到合并序列尾。

《数据结构中常见的几种排序算法》

//归并排序
void Merge(int a[], int left, int mid,int right)
{
	int size = right - left + 1;
	int *temp = (int *)malloc(size*sizeof(int));
	int i = left;
	int j = mid + 1;
	int index = 0;
	while (i <= mid && j <= right)
	{
		temp[index++] = a[i] < a[j] ? a[i++] : a[j++];
	}
	while (i <= mid)
	{
		temp[index++] = a[i++];
	}
	while (j <= right)
	{
		temp[index++] = a[j++];
	}
	for (int k = 0; k < size; k++)
	{
		a[left++] = temp[k];
	}
}



void MergeSort(int a[], int left, int right)
{
	if (left == right) return;
	int mid = left + ((right - left) >> 1);
	MergeSort(a, left, mid);
	MergeSort(a, mid + 1, right);
	Merge(a, left, mid, right);
}

堆排序

利用堆 这种数据结构所设计的一种排序算法,也是选择排序的一种。

a 可以利用数组的特点快速定位指定索引的元素

b 堆分为大堆小堆 是完全二叉树

大堆 要求每个节点的值都不大于其父节点的值即a[parent]>=a[i]

在数组的非降序排序中,需要使用的就是大堆

大堆中最大值肯定是在 堆顶的

步骤:a 首先将序列构建称为大顶堆;

(这样满足了大顶堆那条性质:位于根节点的元素一定是当前序列的最大值)

b 取出当前大顶堆的根节点,将其与序列末尾元素进行交换;

(此时:序列末尾的元素为已排序的最大值;由于交换了元素,当前位于根节点的堆并不一定满足大顶堆的性质)

c 重复a.b步骤,直至堆中只有1个元素为止

 

《数据结构中常见的几种排序算法》

//堆排序
void HeapModify(int a[], int i, int size)//向下调整
{
	int left_child = i * 2 + 1;//左孩子索引
	int right_child = i * 2 + 2;//右孩子索引
	int max = i;//选出当前节点与其左右孩子三者中最大值
	if (left_child<size && a[left_child]>a[max])
		max = left_child;
	if (right_child<size && a[right_child]>a[max])
		max = right_child;

	if (max != i)
	{
		swap(a, i, max);//将当前节点与其最大子节点进行交换
		HeapModify(a, max, size);// 递归调用 继续从当前节点向下进行堆调整
	}
}
//建堆
int BuildHeap(int a[], int size)
{
	int heap_size = size;
	for (int i = (heap_size >> 1) - 1; i >= 0; --i)
	{
		HeapModify(a, i, heap_size);
	}
	return heap_size;
}
void HeapSort(int a[], int size)
{
	int heap_size = BuildHeap(a, size);//建大堆
	while (heap_size > 1)
	{
		swap(a, 0, --heap_size);
		HeapModify(a, 0, heap_size);//从新的堆顶元素开始向下调整,时间复杂度为O(lgn)
	}
}

选择排序

排序原理:

a 首先在未排序序列中找到最小元素,存放到排序序列的起始位置,

b 再从未排序元素中继续寻找最小元素,然后放到排序序列尾

c 一次类推 直到所有元素都排序完毕

《数据结构中常见的几种排序算法》

 

//选择排序
void SelectSort(int a[], int size)
{
	for (int i = 0; i < size-1; ++i)
	{
		int min = i;
		for (int j = i + 1; j < size - 1; ++j)
		{
			if (a[j] < a[min])
				min = j;
		}
		if (min != i)
			swap(a, min, i);
	}
}

冒泡排序

排序原理:

a)重复地走访过要排序的数列,一次比较两个元素,

如果他们顺序错误就把他们交换过来

b)走访数列的工作就是重复地进行 直到没有再需要交换,

也就是说该数列已经排序完成

c) 这个算法就是因为越小的元素会经由交换慢慢浮到数列的顶端

步骤:

1)将序列当中的左右元素,依次比较,保证右边的元素始终大于左边的元素;( 第一轮结束后,序列最后一个元素一定是当前序列的最大值;)

2)对序列当中剩下的n-1个元素再次执行步骤1。

3)持续每次对越来越少的元素进行重复上面的步骤直到没有任何一对数字需要比较

《数据结构中常见的几种排序算法》

void swap(int a[], int i, int j)
{
	int temp = a[i];
	a[i] = a[j];
	a[j] = temp;
}
//冒泡
void BubbleSort(int a[], int size)
{
	for (int i = 0; i < size - 1; ++i)
	{
		for (int j = 0; j < size - 1 - i; ++j)
		{
			if (a[j] < a[j + 1]){
				swap(a,j,j+1);
			}
		}
	}
}
//鸡尾酒排序 分两部分1)将最大的元素放在后面 2)将最小的元素放在前面
void Bubblesort(int a[], int size)
{
	int left = 0;
	int right = size - 1;
	while (left<right)
	{
		for (int i = 0; i < right; i++)
		{
			if (a[i]<a[i + 1])
			{
				swap(a, i, i + 1);
			}
		}
		right--;
		for (int j = right; j>left; j--)
		{
			if (a[j - 1] < a[j])
			{
				swap(a, j - 1, j);
			}
		}
		left++;
	}
}

基数排序

基数排序:通过序列中各个元素的值,对排序的N个元素进行若干趟的“分配”与“收集”来实现排序。

分配:我们将L[i]中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中

收集:当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列L[ ]

对新形成的序列L[]重复执行分配和收集元素中的十位、百位…直到分配完该序列中的最高位,则排序结束

《数据结构中常见的几种排序算法》

 

附加上各种排序算法的稳定性以及空间/时间复杂度

《数据结构中常见的几种排序算法》

 

 

 

 

 

 

 

 

 

 

 

 

 

    原文作者:排序算法
    原文地址: https://blog.csdn.net/qq_39380590/article/details/82113421
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞