当前位置:   article > 正文

8大排序算法(超详细,算法步骤+代码)_计算机排序算法流程

计算机排序算法流程
  1. 排序的分类

按照排序过程中所依据的原则的不同可以分类为:

插入排序:直接插入排序 希尔排序

►交换排序:冒泡排序 快速排序

选择排序:简单选择排序 堆排序

►归并排序

►基数排序

  1. 排序算法比较

从平均情况看:堆排序、归并排序、快速排序胜过希尔排序

从最好情况看:冒泡排序和直接插入排序更胜一筹。

从最差情况看:堆排序和归并排序强过快速排序。

虽然直接插入排序和冒泡排序速度比较慢,但是当初始序列整体或局部有序是,这两种算法的效率比较高。当初始序列整体或局部有序时,快速排序算法效率会下降。当排序序列较小且不要求稳定性是,直接排序效率较好;要求稳定性时,冒泡排序法效率较好。
8大排序算法:插入、冒泡、选择、希尔、归并(外部)、快速、堆、基数
内部排序:排序期间,元素全部放在内存中的排序
外部排序:排序期间,元素无法全部存放在内存中,必须在排序过程中根据要求不断地进行内外存之间移动排序
稳定性:指的是经过排序后,值相同的元素保持原来顺序的相对位置不变
1.冒泡排序:(全部按照升序排列
思想:从第一个数开始,两两比较,如果第二个数比第一个数大,交换第一个数与第二个数的位置,相当于大泡泡往下沉了,一趟完了之后,最大的那个数就在最后一个位置了;然后继续比较,接下来是第二大的数字排在倒数第二个位置。。。

void BubbleSort(int arr[], int size)
{
	for (int i = 0; i < size-1; i++)
	{
		for (int j = 0; j < size-i-1; j++)
		{
			if (arr[j]>arr[j + 1])
			{
				int temp;
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j + 1] = temp;
			}
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.插入排序
思想:给一组数据,先对前一个元素进行排序(就是第一个数),然后对前两个数进行排序,再对前三个数进行排序。。。。一直到排完为止。

void InsertSort(int arr[], int size)
{
	int tmp,i,j;
	for (i = 1; i < size; i++)
	{
		tmp = arr[i];
		for (j = i - 1; j >= 0; j--)
		{
			if (tmp < arr[j])//如果第二个数比第一个数大的话,就要交换
			{
				arr[j + 1] = arr[j];
			}
			else
			{
				break;
			}
			
		}
		arr[j + 1] = tmp;
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

3.希尔排序
希尔排序是在直接插入排序的基础上进行的改进:
1)首先计算整个数组大小,然后除以2,比如说原始数组中有8个元素,除以2之后那么跨度就是4,第1个元素和跨度为4的元素为一组,第2个元素与跨度为4的元素为一组,以此类推。这样下来,就会是两两一组,一共有4组,每组进行排序。
2)然后跨度为2,重复第1)步的过程
3)然后跨度为1,重复第1)步的过程

示例中所使用的分组跨度(4,2,1),被称为希尔排序的增量,增量的选择可以有很多种,我们在示例中所用的逐步折半的增量方法,是Donald Shell在发明希尔排序时提出的一种朴素方法,被称为希尔增量。

void ShellSort(int arr[], int size)
{
	int i, j,tmp,h;
	for (h = size / 2; h > 0; h /= 2)
	{
		for (i = h; i < size; i++)
		{
			tmp = arr[i];
			for (j = i - h; j>=0; j-=h)
			{
				if (tmp < arr[j])
				{
					arr[j + h] = arr[j];
				}
				else
				{
					break;
				}
			}
			arr[j + h] = tmp;
		}
	
	}
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

4.快速排序
算法思想:
分治法:比大小,再分区
1.从数组中选择一个数作为基准数,通常选择的是第一个数
2.分区:把比基准数小的数放在基准数的左边,把比基准数大的数放到基准数的右边。
3.再对左右区间重复第二步,直到区间中只有一个数。
实现思路:
挖坑填数
1.将基准数挖出形成第一个坑
2.由后向前找出比它小的数,找到后挖出此数填到前一个坑中
3.由前向后找比它大或等于的数,找到后挖出此数填到前一个坑中
4.重复2,3两步。

【5,3,9,1,6,7,2,4,0,8】
数组下标 0 1 2 3 4 5 6 7 8 9
数组元素 5 3 9 1 6 7 2 4 0 8
坑位 坑位1 坑位3 坑位5 坑位7 坑位6 坑位4 坑位2
0 4 2 5 7 6 9
0 3 4 1 2 5 7 6 9 8

通俗解释:就是先找一个基准数,通常是第一个数,然后把这个数对应的位置置为坑位1,然后先在右边找比基准数小的数,找到之后,把它对应的位置置为坑位2,然后把空位2的数放到坑位1中。然后从左边找比基准数大的数,找到的话记为坑位3,把坑位3的数,放到坑位2中。。。一直重复这个动作,最后只剩下一个坑位的时候,把基准数放到那个坑位中,这样就会使得基准数左边都是比它小的数,基准数的右边都是比它大的数。
这样的话,就是需要两个循环,分别是从左往右的循环和从右往左的循环。

void QuickSort(int arr[],int left, int right)
{
	int tmp, tmp2;
	int i, j;
	i = left;
	j = right;
	if (left >= right)
	{
		return;
	}
	int base = arr[left];
	while (i < j)
	{
		while (arr[j]>=base&&i<j)//从右向左找如果比基准数大的话,就往前移一个,如果比基准值小就挖坑
		{
			j--;
		}
		while (arr[i] <= base&&i<j)//从左往右找,如果比基准数小的话,就往后移一个,如果比基准数大的就挖坑
		{
			i++;
		}
		if (i < j)
		{
	    tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
		}
	
		
	}
	tmp2 = arr[i];
	arr[i] = arr[left];
	arr[left] = tmp2;
	QuickSort(arr,left, i - 1);
	QuickSort(arr, i + 1, right);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

5.选择排序
遍历数组,每次把最大的一个元素放到最后一个位置,直到还剩下一个数字,此时数组就是有序的。

void SelectSort(int arr[], int size)
{
	int i, j;
	for (i = size; i > 1; i--)
	{
		int max = 0;
		for (j = 0; j < i; j++)
		{
			if (arr[j]>arr[max])
			{
				max = j;
			}
		}
		int tmp = arr[i - 1];
		arr[i - 1] = arr[max];
		arr[max] = tmp;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

6.归并排序

归并排序的核心思想在于“分治”,所谓“分治”就是分而治之,把一个大的问题划分为许多小问题去解决,在处理数组排序时,如果数组中只有一个元素,那么我们就可以认为这个数组就是有序的,如果数组中有两个元素,那么最多只需比较一次就可得到排好序的数组,这就是分治算法的核心

归并排序就是,假设我们要排【3,2,5,1,4】这个数组,那么一般按照前原数组分成两部分的思想,这里可把原数组分成【3,2,5】和【1,4】然后可以继续分,直到分到单个元素为止

显然这个过程可以分为两个部分:
1.拆分过程:很明显适合使用递归写法,具体就是数组大小除2,一直这样,直到数组中元素个数为1
2.合并过程。
合并过程可分为3个部分,首先是把原数组分为左数组和右数组。第二部分就是根据大小进行拍戏,如果左数组中的元素大小小于右数组元素大小,就把左数组中元素放入数组中,反之将右数组中的元素放入数组中。第三部分:比较之后,可能左数组和右数组中有剩余的元素,将数组中剩余的元素放入数组中就可得到有序的数组。

void Divide(int arr[], int start, int end)
{
	while (start >= end)
	{
		return;
	}
	int mid = (start + end) / 2;
	Divide(arr, start, mid);
	Divide(arr, mid + 1, end);
}
void MergeSort(int arr[], int start, int mid, int end)//这块是合并过程
{
	//第一部分是将原数组中的元素分别存在左数组和右数组中
	int left[256] = { 0 };//左数组
	int right[256] = { 0 };//右数组
	int leftsize = mid - start + 1;
	int rightsize = end - mid;
	for (int i = 0; i < leftsize; i++)
	{
		left[i] = arr[i];
	}
	for (int j = 0; j < rightsize; j++)
	{
		right[j] = arr[j];
	}

	//第二部分主要是比较大小了
	int i=0, j=0, k=0;
	for (k = start; i < leftsize&&j < rightsize; ++k)
	{
		if (left[i] < right[i])
		{
			arr[i] = left[i];
			i++;
		}
		else
		{
			arr[j] = right[j];//i和j的范围之和就是整个数组的大小,因此不会出现覆盖的情况
			j++;
		}
	}
	//第三部分就是把左数组和右数组中剩下的元素放到arr中
	if (i < leftsize)//如果左数组中元素有剩余
	{
		for (; i < leftsize; i++)
		{
			arr[i] = left[i];
			++k;
		}
	}
	if (j < rightsize)//如果右数组中元素有剩余
	{
		for (; j < rightsize; j++)
		{
			arr[j] =right[j];
			++k;
		}
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

7.堆排序
主要要依据堆结构,堆是一种具有特殊性质的完全二叉树(就是连着排的,中间没有空的),这个特殊性质就是,如果是大堆的话,那么根结点的值要大于两个孩子结点的值。如果是小堆的话,那么根节点的值要小于左右孩子结点的值。
【5,2,7,3,6,1,4】
思路:就是不断调整结点的位置,如果是升序的话,就调整成大堆的形式,调整成大堆的形式之后,此时堆顶元素就是最大的一个,取出堆顶的元素放到数组中的最后一个位置,此时堆顶就会空出一个位置,然后把二叉树的最后一个位置的元素放到堆顶,然后继续调整,使之成为一个大堆,然后取出堆顶元素,一直取,直到剩余一个元素,此时数组中就是一个升序序列。
写代码的时候,分成两个部分,首先要建堆,建堆的过程就是从最后一个非叶子结点开始向上调整,因为叶子结点最终都是要放到根节点的,所以就不用从叶子结点开始了,当然从叶子结点开始也是可以的,调整完了之后,将最后一个叶子结点与根节点进行交换,交换完了还得调整,因为可能此时已经不满足堆的性质了。
第二部分是具体的向上调整的过程:
这里有几点:
如果当前要访问的结点索引为i的话,那么
父节点索引:(i-1)/2
左孩子索引:2i+1;
右孩子索引:2
i+2;
首先要判断如果左孩子索引小于数组大小,说明没调整完,如果左孩子数值大于最大索引位置的数值是,要把左孩子索引赋值给最大值索引。同理,右孩子也是一样。完了之后,如果发现此时最大值索引是不等于传入那个索引,也就是根节点索引的时候,说明需要交换位置了,就把最大值索引结点值与arr[index]值进行交换,交换完了再调整(只要交换了,就需要调整)

void UpAdjust(int arr[], int index, int size)
{
	int leftchild = 2 * index + 1;
	int rightchild = 2 * index + 2;
	int maxindex = index;
	if (leftchild < size&&arr[maxindex] < arr[leftchild])
	{
		maxindex = leftchild;
	}
	if (rightchild < size&&arr[maxindex] < arr[rightchild])
	{
		maxindex = rightchild;
	}
	if (maxindex != index)
	{
		swap(arr[maxindex], arr[index]);
		UpAdjust(arr, maxindex, size);
	}
}
void HeapSort(int arr[], int size)
{
	for (int i = size - 1; i >= 0; i--)
	{
		UpAdjust(arr, i, size);
	}
	for (int i = size-1; i >= 0; i--)
	{
		swap(arr[0], arr[i]);
		UpAdjust(arr, 0, i);
	}
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

8.基数排序
就是比如说一组数据【73,22,93,43,55,14,28,65,39,81】
先按照个位排序:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39
然后把这些重新排序起来【81 22 73 93 43 14 55 65 28 39】
按照十位排序:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93
然后把这些排序起来【14 22 28 39 43 55 65 73 81 93】

int maxbit(int data[], int n) 
{
    int d = 1; //保存最大的位数
    int p = 10;
    for(int i = 0; i < n; ++i)
    {
        while(data[i] >= p)
        {
            p *= 10;
            ++d;
        }
    }
    return d;
}
void radixsort(int data[], int n) //基数排序
{
    int d = maxbit(data, n);
    int tmp[n];
    int count[10]; //计数器
    int i, j, k;
    int radix = 1;
    for(i = 1; i <= d; i++) //进行d次排序
    {
        for(j = 0; j < 10; j++)
            count[j] = 0; //每次分配前清空计数器
        for(j = 0; j < n; j++)
        {
            k = (data[j] / radix) % 10; //统计每个桶中的记录数
            count[k]++;
        }
        for(j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
        for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
        {
            k = (data[j] / radix) % 10;
            tmp[count[k] - 1] = data[j];
            count[k]--;
        }
        for(j = 0; j < n; j++) //将临时数组的内容复制到data中
            data[j] = tmp[j];
        radix = radix * 10;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/759693?site
推荐阅读
相关标签
  

闽ICP备14008679号