轻松理解各种排序算法(附C++代码)

排序简介

简单排序:插入排序,选择排序,冒泡排序。(只要掌握了冒泡排序即可)他们的时间复杂度都为O(N^2)。
插入排序:从第一个数据开始,把第一个数据放入有序队列(有序队列一开始为空)进行排序,依次进行,直到排序完整。(把未排序的数据放入有序队列中)
希尔排序: 插入排序的改版。也就是多个数据进行插入排序。
选择排序:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
冒泡排序:顾名思义,把一个最小(大)的数据冒到最上面去,在冒的过程中,每次只比较两个数据,谁小(大)谁进入泡泡继续往上冒。
代码:

#include <stdio.h>
void bubble_sort(int arr[], int len) {
    int i, j, temp;
    for (i = 0; i < len - 1; i++)
        for (j = 0; j < len - 1 - i; j++)
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
}
int main() {
    int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    bubble_sort(arr, len);
    int i;
    for (i = 0; i < len; i++)
        printf("%d ", arr[i]);
    return 0;
}

重点:
快速排序: 跟冒泡相似,但是比冒泡快很多,因为快排是左右开弓,首先找一个基数k,以k为基准,把整个数据分为两个部分,左边:全部小于k。右边:全部大于k。然后将左右边的数据以同样的方法周而复始(即递归)。
代码如下:

`#include<iostream>
using namespace std;
//函数声明
void quick_sort(int* a, int begin, int end);

int main()
{
	int a[10];
	for (int i = 0; i < 10; i++)
	{
		cin >> a[i];
	}
	quick_sort(a, 0, 9);
	for (int i = 0; i < 10; i++)
	{
		cout << a[i];
	}
	return 0;
}
void quick_sort(int* a, int begin, int end)
{
	int k;//基数
	int n, m;//哨兵
	n = begin;
	m = end;
	k = a[begin];
	if (n > m)//递归的终止条件
	{
		return;
	}
	while (n != m)
	{
		while (m > n && a[m] > k)
			m--;
		while (n < m && a[n] < k)
			n++;
		if (n != m)
		{
			int l;
			l = a[n];
			a[n] = a[m];
			a[m] = l;
		}
	}
	a[n] = k;//把基数放到正确的位置
	k = n;//以基数分界点,分别递归解决左右两边的排序
	quick_sort(a, 0, k - 1);
	quick_sort(a, k+1, end);
}`

快排2.0: 简单的快排的时间复杂度为O(N^2),这里的加强版的时间复杂度可以降为O(N * logN)。我们利用分治的思想:
第一步:从数列中随机选取一个数与数列的最后一个数交换(降时间复杂度的关键),将这个选取的数作为基准,把数列分成三部分,小于基准的放左边,等于的放中间,大于的放右边,这个过程我们称为partition。之后我们递归,用左边和右边的重复进行partition。
第二部:如何定义partition的部分,首先我们定义一个小于区域的右边界,再定义一个大于区域的左边界,i = 0,1>>当第i个数小于基准时,用第i个数跟小于区域右边界+1个数进行交换,再将小于区域的右边界扩大一位,i++;2>>当第i个数等于基准时,i++;3>>当第i个数大于基准时,用第i个数跟大于区域左边界-1个数进行交换,再将大于区域的左边界减小一位,i不变;然后将基准的数与大于区域的左边界的数进行交换,最后将返回等于区域的左边界和右边界;
代码:

#include<iostream>
#include<ctime>
using namespace std;
#define random(x) (rand()%x)
void sp(int* a, int l, int r)//交换函数
{
	int tmp = 0;
	tmp = a[l];
	a[l] = a[r];
	a[r] = tmp;
}

int* partition(int* a, int l, int r)
{
	int p[2] = { 0 };//存储等于区域的左右边界
	int less = l - 1;//左边界
	int more = r;//右边界
	while (l < more)
	{
		if (a[l] < a[r])//第一种情况
			sp(a, ++less, l++);
		else if (a[l] > a[r])//第二种情况
			sp(a, --more, l);
		else//相等时
			l++;
	}
	sp(a, more, r);
	p[0] = less + 1;
	p[1] = more;
	return p;
}

void quick_sort(int* a, int l, int r)
{
	if (l < r)
	{
		srand((int)time(0));
		int s = l+ random(r-l+1);//降低复杂度的关键点
		sp(a, s, r);
		int* p;
		p = partition(a, l, r);
		quick_sort(a, l, p[0] - 1);
		quick_sort(a, p[1] + 1, r);
	}
}

int main()
{
	int a[6] = { 3,2,5,4,7,6 };
	quick_sort(a, 0, 5);//这里的a是递归的非决定性参数
	for (int i = 0; i <= 5; i++)
	{
		cout << a[i] << endl;
	}
}

归并排序: 利用了分治的思想和递归。我们的整体思想是先把一个数列分成两半,保证数列的两半都是有序的(merge)。在写递归时,把递归的流程图画出来,从大局观看更容易理解。

#include<iostream>
using namespace std;
void merge_wp(int* a, int l, int m, int r)
{
	int* h = new int[r - l + 1];//存储目前归并好的临时数组
	int p1 = l;//从左边一半的第一个开始的标记
	int p2 = m + 1;//从右边一半的第一个开始的标记
	int i = 0;//临时数组的下标
	while (p1 <= m && p2 <= r)//当标记都不超过各自那半数组的界限时
		h[i++] = a[p1] <= a[p2] ? a[p1++] : a[p2++];//标记各自往后移动进行排序
	while (p1 <= m)
		h[i++] = a[p1++];//当左边的数组还剩下元素,则依次添加到后面
	while (p2 <= r)
		h[i++] = a[p2++];//同理
	for (int j = 0; j <r-l+1; j++)
		a[l+j] = h[j];
	delete[] h;
}

void merge_sort(int* a, int l, int r)
{
	if (l == r)//如果只有一个元素则返回
		return;
	int mid = l + ((r - l) >> 1);//这样取中点不会出现越界
	merge_sort(a, l, mid);//利用merge_wp保证左边有序
	merge_sort( a, mid + 1, r);//利用merge_wp保证右边有序
	merge_wp( a, l, mid, r);//归并操作
}

int main()
{
	int a[6] = { 3,2,5,4,7,6 };
	merge_sort(a, 0, 5);//这里的a是递归的非决定性参数
	for (int i = 0; i <= 5; i++)
	{
		cout << a[i] << endl;
	}

}

堆排序: 首先要了解堆结构,堆结构其实就是STL中的优先级队列(priority_queue),在一个数列当中,将最大的数放在完全二叉树的顶点,从大到小构成一颗完全二叉树,形成大根堆,反则是小根堆,在这种大根堆结构当中我们可以知道最大的数就是第一个位置的数,堆排序就是在大根堆的基础上多一步操作(heapify),把第一个位置的数与最后一个位置的数进行交换,再把最后一个位置之前的树进行大根堆排序,周而复始,数列就能升序排序。

#include<iostream>
using namespace std;

void swap(int* a, int i,int j)
{
	int tmp;
	tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;
}

void heapinsert(int* a, int index)//从index位置插入数据,形成大根堆(往上移动)
{
	while (a[index] > a[(index - 1) / 2])//只要该位置的数据大于父节点就交换
	{
		swap(a, index, (index - 1) / 2);
		index = (index - 1) / 2;
	}
}

void heapify(int* a, int index, int heapsize)//从index位置的数开始往下移动
{
	int left = index * 2 + 1;//左孩子的下标
	while (left < heapsize)//有孩子才往下移动
	{
		int largest = left + 1 < heapsize && a[left + 1] > a[left] ? left + 1 : left;//存在右孩子且右孩子大于左孩子 选出最大的数的位置
		largest = a[largest] > a[index] ? largest : index;//看孩子是否大于父节点index
		if (largest == index)//当移动到合适的位置停止
			return;
		swap(a, largest, index);
		index = largest;
		left = index * 2 + 1;
	}
}

int main()
{
	int a[6] = { 4,3,7,8,1,9 };
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		heapinsert(a, i);//形成大根堆
	}
	int heapsize = sizeof(a) / sizeof(a[0]);//限制heapify的个数,heapity的作用就是找出一个最大的数 放在最后面,下一次就对其他的heapify
	swap(a, 0, --heapsize);
	while (heapsize > 0)
	{
		heapify(a, 0, heapsize);
		swap(a, 0, --heapsize);
	}
	for (int i = 0; i < 6; i++)
	{
		cout << a[i] << endl;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值