八大排序算法

区分方法:

如果按照难易程度来区分:

(4个简单排序算法中除了基数排序,剩下3个代码都是双重for循环)
4个简单:直接插入排序 简单选择排序 冒泡排序 基数排序
4个难:希尔排序 堆排序 归并排序 快速排序

如果按照稳定性来区分:
4个稳定:直接插入排序 冒泡排序 归并排序 基数排序
4个不稳定:希尔排序 简单选择排序 堆排序 快速排序

直接插入排序:

时间复杂度O(n^2)  空间复杂度O(1)  稳定性:稳定

//直接插入排序  时间复杂度O(n^2)  空间复杂度O(1)  稳定性:稳定
void Insert_Sort(int arr[], int len)
{
    //for (int i = 0; i < len - 1; i++)//控制的是趟数 如果有len个值,需要len-1趟即可
    for (int i = 1; i < len; i++)
    {
        //此时i下标指向的就是我们这一趟中的待插入值
        int tmp = arr[i];

        int j = i - 1;
        for (; j >= 0; j--)//内部for循环控制的是 单独一趟中,对于已//排序好的序列从右至左的遍历
        {
            if(arr[j] > tmp)
                arr[j+1] = arr[j];
            else
                //将值挪回去结束
                break;
        }
        arr[j + 1] = tmp;
    }
}

void Show(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);

	printf("\n");

}

希尔排序:

时间复杂度O(n^1.5)  空间复杂度O(1)  稳定性:不稳定

//希尔排序  时间复杂度O(n^1.5)  空间复杂度O(1)  稳定性:不稳定
void Shell(int arr[], int len, int gap)//gap是当前增量大小
{
    for (int i = gap; i < len; i++)//i控制的是向前插入调整的值的下标
    {
        //此时i下标指向的就是我们这一趟中的待插入值
        int tmp = arr[i];

        int j = i - gap;
        for (; j >= 0; j-=gap)//内部for循环控制的是 单独一趟中,对于已//排序好的序列从右至左的遍历
        {
            if (arr[j] > tmp)
                arr[j + gap] = arr[j];
            else
                //将值挪回去结束
                break;
        }
        arr[j + gap] = tmp;
    }
}

void Shell_Sort(int arr[], int len)
{
    int gap[] = { 5,3,1 };//增量数组
    for (int i = 0; i < sizeof(gap) / sizeof(gap[0]); i++)
    {
        Shell(arr, len, gap[i]);
    }
}

简单选择排序:

时间复杂度O(n^2)  空间复杂度O(1)  稳定性:不稳定

void Intersect_Sort(int arr[], int len)
{
    for (int i = 0; i < len - 1; i++)//控制的是趟数
    {
        int min = i;
        for (int j = i + 1; j < len; j++)//找到这一趟中,待排序序列中最小值所在位置
        {
            if (arr[j] < arr[min])
            {
                min = j;
            }
        }
        //和此时待排序序列第一个值进行交换
        if (min != i)//arr[min]  arr[i]
        {
            int tmp = arr[min];
            arr[min] = arr[i];
            arr[i] = tmp;
        }
    }
}

void Show(int arr[], int len)
{
    for (int i = 0; i < len; i++)
        printf("%d ", arr[i]);

    printf("\n");
}

冒泡排序及其优化:

时间复杂度O(n^2)  空间复杂度O(1)  稳定性:稳定

void Bubble_Sort(int arr[], int len)
{
    for (int i = 0; i < len - 1; i++)//控制的是趟数
    {
        //j指向的是两两比较中偏左边的那个值
        for (int j = 0; j + 1 <= len - 1 - i; j++)//控制的是这一趟中,对于待排序序列的从前向后的两两比较
        {
            if (arr[j]>arr[j+1])//如果左边 > 右边 则交换
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}
//冒泡排序的优化
void Bubble_Sort2(int arr[], int len){
    for (int i = 0; i < len - 1; i++)//控制的是趟数
    {
        bool tag = true;//标签申请在内部for循环上面
        //j指向的是两两比较中偏左边的那个值
        for (int j = 0; j + 1 <= len - 1 - i; j++)//控制的是这一趟中,对于待排序序列的从前向后的两两比较
        {
            if (arr[j] > arr[j + 1])//如果左边 > 右边 则交换
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                tag = false;
            }
        }

        if (tag)
        {
            return;
        }
    }
}

堆排序:

选择类排序

默认升序用大顶堆,默认降序用小顶堆

二叉树子节点推父节点:(i-1)/2

二叉树父节点推子节点:2i+1,2i+2

大顶堆:

是一颗完全二叉树,并且父节点的值大于子节点的值

初始状态:将一维数组arr得到,然后将这个数组臆想成完全二叉树

第一步:

将这棵完全二叉树调整为大顶堆(第一次调整需要由内到外的调整)-->从最后一个非叶子节点构成的子树开始调整,由右向左,自下向上

根据定义可知:第一次由内到外的调整调整子树顺序为:红->绿->橙->黄->黑

第二步:

将此时大顶堆的根节点(最大值)和其最后一个节点值进行交换,然后再把最后一个节点断开连接

第三步:

第二步头尾交换之后断开尾节点,然后重新调整为大顶堆(只需要调整最外层框即可)

第四步:

重复执行2,3步,直到大顶堆只剩下一个节点停止

小顶堆:

是一颗完全二叉树,并且父节点的值小于子节点的值

排序代码实现:

1.将此时要调整的子树的根节点的值,拷贝一份给tmp(防止一会被覆盖)

2.找到当前空白格子,然后判断其有无孩子,如果有则进一步找到其较大的孩子,然后将较大孩子的值和tmp的值进行比较

3.如果较大孩子的值大于tmp的值,则向上顶(将此时较大孩子的挪动到其父节点位置)
4.如果较大孩子的值向上顶,则较大孩子位置会出现新的空白格子,重复上面过程,继续判断其是否有孩子,如果有则继续找其较大孩子

5.如果较大孩子的值并不大于tmp的值,则此时可以将tmp的值挪回去了

6.如果空白格子就没有孩子节点,则认为触底了,则此时也可以将tmp的值挪回去了
7.tmp的值挪回去之后,整体调整结束

//堆排序  时间复杂度O(nlogn)  空间复杂度O(1)  稳定性:不稳定
void Heap(int arr[], int begin, int end) {//begin为当前子树的根节点,end当前小树下最后一个节点序号
	int parent = begin;
	int child = 2 * parent + 1;
	while (child <= end) {
		//判断右孩子是否存在,如果存在和左孩子比较大小
		if (child + 1 <= end && arr[child + 1] > arr[child]) {
			child++;
		}
		//较大孩子的值与根节点比较
		if (arr[child] > arr[parent]) {
			int tmp = arr[parent];
			arr[parent] = arr[child];
			arr[child] = tmp;
			parent = child;
			child = 2 * parent + 1;
		}
		else {
			break;
		}
	}
}

void HeapSort(int arr[], int len) {
	for (int i = (len - 1 - 1) / 2; i >= 0; i--) {
		Heap(arr, i, len - 1);
		for (int j = len - 1; j >= 1; j++) {
			int tmp = arr[0];
			arr[0] = arr[j];
			arr[j] = tmp;
			Heap(arr, 0, len - 1);
		}
	}
}

归并排序:

时间复杂度O(nlogn)  空间复杂度O(n)  稳定性:稳定

void Merge(int arr[], int left, int mid, int right) {
	assert(arr);
	int* brr = (int*)malloc((right - left + 1) * sizeof(int));
	if (brr == NULL) {
		exit(EXIT_FAILURE);
	}
	int i = left;
	int j = mid + 1;
	int k = 0;//用来指向brr数组用来接受元素的格子下标
	while (i <= mid && j <= right) {
		if (arr[i] <= arr[j]) {
			brr[k++] = arr[i++];
		}
		else {
			brr[k++] = arr[j++];
		}
	}
	while (i <= mid) {
		brr[k++] = arr[i++];
	}
	while (j <= right) {
		brr[k++] = arr[j++];
	}
	//这时两个组的合并结果就在brr里面,最后再将brr的数据同步还给arr[left, right]上即可
	/*for (int i = left; i <= right; i++)
	   arr[i] = brr[i - left];*/
	for (int k = 0; k <= right - left + 1 - 1; k++)
		arr[left + k] = brr[k];

	free(brr);
	brr = NULL;
}

void Divide(int arr[], int left, int right){
	int mid = (left + right) / 2;
	if (left < right){
		//左半边[Left, mid]  右半边[mid+1,right]
		Divide(arr, left, mid);//左半边继续递归分
		Divide(arr, mid + 1, right);//右半边继续递归分
		Merge(arr, left, mid, right); //回退的左半边和右半边进行合并
	}
}

void Merge_Sort(int arr[], int len)
{
	//执行递归分函数
	Divide(arr, 0, len - 1);
}

基数排序:

 时间复杂度O(d(n+r))  空间复杂度O(N)  稳定性:稳定
对所有的待排序数据从最低位(个位)开始处理,此时数据只看最低位有序,然后按十位处理,然后按百位处理
处理规则:将数据按照对应最低位的值插入到对应的队列中,再将所有队列中的值全部取出,取出顺序为0-9,放回数组中

//找到数组中最大值,返回最大值的位数
int Get_MaxNum_Figure(int arr[], int len) {
	int max = arr[0];
	for (int i = 1; i < len; i++) {
		if (arr[i] > max) {
			max = arr[i];
		}
	}
	if (max == 0) {
		return 1;
	}
	int count = 0;
	while (max != 0) {
		max /= 10;
		count++;
	}
	return count;
}

//根据给定的值和对应的位,将这个值的这个位是多少返回
int Get_Num_figureNum(int num, int fig) {
	while (fig > 0) {
		num /= 10;
		fig--;
	}
	return num % 10;
	//return (arr >> fig) & 1;
}

//按照传递进来的位,将所有数据处理一遍
#include<queue>//队列头文件
void Radix(int arr[], int len, int fig) {
	//std::queue<int> qu;//申请一个队列
	std::queue<int> quarr[10];//十个格子的数组,每一个格子是一个队列

	for (int i = 0; i < len; i++) {
		int index = Get_Num_figureNum(arr[i], fig);
		quarr[index].push(arr[i]);
	}
	//0-9中取值到arr
	int k = 0;
	for (int i = 0; i <= 9; i++) {
		while (!quarr[i].empty()) {
			arr[k] = quarr[i].front();
			quarr[i].pop();
			k++;
		}
	}
}

void RadixSort(int arr[], int len) {
	int maxnum_figure = Get_MaxNum_Figure(arr, len);
	for (int i = 0; i < maxnum_figure; i++) {
		Radix(arr, len, i);//i=0为个位处理...
	}
}

快速排序:

平均时间复杂度O(nlogn)  最坏时间复杂度O(n^2) 空间复杂度O(logn)  稳定性:不稳定

以给定的基准值为中心,将待排序序列分成两部分,左边小于基准值,右边大于

递归实现:

//单次划分函数
int Partition(int arr[], int left, int right) {
	int tmp = arr[left];
	while (left < right) {
		while (left < right && arr[right] >= tmp) {
			right--;
		}
		//内部循环退出情况1:指针相遇了
		if (left == right) {
			break;
		}
		//内部循环退出情况2:在指针相遇前找到了一个小于或者等于tmp的值
		arr[left] = arr[right];

		while (left < right && arr[left] <= tmp) {
			left++;
		}
		if (left == right) {
			break;
		}
		arr[right] = arr[left];
	}
	arr[left] = tmp;
	return left;
}
//递归函数
void Quick(int arr[], int left, int right) {
	if (left >= right) {
		return;
	}
	int par = Partition(arr, left, right);
	
	Quick(arr, left, par - 1);
	Quick(arr, par + 1, right);
}

void QuickSort(int arr[], int len) {
	Quick(arr, 0, len - 1);
}

非递归实现:

//非递归,用栈实现
#include<stack>
void QuickSortNo(int arr[], int len) {
	std::stack<int> st;
	st.push(0);
	st.push(len - 1);
	while (!st.empty()) {
		//取出一组左右边界
		int right = st.top();
		st.pop();
		int left = st.top();
		st.pop();
		int par = Partition(arr, left, right);
		//判断左右基准个数
		if (par - 1 > left) {
			st.push(left);
			st.push(par - 1);
		}
		if (par + 1 < right) {
			st.push(par + 1);
			st.push(right);
		}
	}
}

优化:

三数取中法:

//三数取中法
void ThreeNumsGetMid(int arr[], int left, int right)
{
	int mid = (left + right) / 2;

	if (arr[left] > arr[mid])
	{
		int tmp = arr[left];
		arr[left] = arr[mid];
		arr[mid] = tmp;
	}
	//现在可以保证,最左端值和中间值这两个值中的较大值,在mid下标上

	if (arr[mid] > arr[right])
	{
		int tmp = arr[mid];
		arr[mid] = arr[right];
		arr[right] = tmp;
	}
	//现在可以保证,此时最左端值和中间值和最右端值这三个值中的最大值,在right下标上


	if (arr[left] < arr[mid])
	{
		int tmp = arr[left];
		arr[left] = arr[mid];
		arr[mid] = tmp;
	}
	//现在可以保证,三者中的不大不小的那个值,正好在left下标上
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值