IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 21天算法打卡系列(6)——冒泡排序和快速排序 -> 正文阅读

[数据结构与算法]21天算法打卡系列(6)——冒泡排序和快速排序

?
?

活动地址:CSDN21天学习挑战赛

冒泡排序

算法思想,进行n-1趟外循环,也即是第一次将数组的最大值移动到数组尾部,第二次就是第二大,进行n-1次循环是因为每次一个最后进行n-1次之后最后一个元素的位置也就固定了。内循环就是比较两个元素的大小,将大的元素放到小元素的后面,每次循环之后比较的元素就减去一个,因为循环完一次就有一个数组最大值移动到了数组的尾部。
这里加入标志位flag,如果某次循环没有数组元素交换,那么这个数组就已经是有序了

代码实现如下:

#include<stdio.h>

int main()
{
    //冒泡排序   
    int arr[10] = { 32,5,66,77,7,4,97,4,3,0 };
    int flag = 0;
    for (int i = 1; i < 10; i++)
    {
        flag = 0;
        for (int j = 0; j < 10 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j + 1] = tmp;
                flag = 1;
            }
        }
        if (flag == 0)
        {
            break;
        }
    }
    
    return 0;
}

冒泡排序是一个简单的排序算法,写起来很简单,两层for循环搞定,但是这也是一个效率很低的排序算法,无论时在什么情况下他的时间复杂度都是O(N^2)所以在追求效率的代码中不适合使用该算法。

快速排序

快速排序版本1:挖坑法

排序思想:先确定一个关键字key(一般选第一个或者最后一个),然后以该位置为坑,然后通过左右指针开始遍历数组,先在右边找到小于key的值,放到坑里面,然后right变为新的坑,再去左边,找一个大于key的值,放到坑里面,然后left变为新的坑,直到最后left==right跳出循环,这时候再将key放到pivot位置,该位置就是key的最终位置,然后使用分治思想,如果pivot左边时有序的,右边也是有序的那么数组就有序了,所以递归左区间,递归右区间,完成排序。
快速排序类似,二叉树的前序遍历,先解决根,再解决左右子区间(子树)

void QuickSort(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	int left = begin;
	int right = end;
	int key = arr[begin];
	int pivot = begin;
	while(left < right)
	{
		while(begin< right && arr[right] >= key)
		{
			--right;
		}
			arr[pivot] = arr[right];
			pivot = right;
		while(left < right && arr[begin] <= key)
		{
			++left;
		}
			arr[pivot] = arr[left];
			pivot = left;
	}
	arr[pivot] = key;
	QuickSort(arr, begin, pivot - 1);
	QuickSort(arr, pivot + 1, end);
}

快排优化版本(三数取中and小区间优化)

因为我们知道上述快排在极限情况下,比如数组是有序的,每次选取,选左区间就会遍历右区间,这样快速排序的二分就失效了,所以这时候时间复杂度是O(N^2),这时候我们可以使用三数取中法进行优化,就是进入一趟排序之前,先将区间中间位置,开始位置,结束位置,他们三个数的中间大小的那个数与第一个数字交换,这样如果数组是有序的,就可以避免最坏情况的出现,因为每次将数组中间那个值换到begin的位置当作key,就可以使得每次排序后接近二分。

但是可能有人还会想这样一个问题,比如,多加了一个求三数取中的函数,函数的调用栈帧的创建是不是会增加时间的消耗呢?例如100w个数,会多100w次函数调用,说答案,其实是不会的,因为cpu对于函数调用的优化是很快的,就算是多调用了100w次,也不会差距几毫秒,但是对于这个问题,我们还有一个优化,就是小区间优化,比如我们递归的时候100w个数,每次二分到最后三次的时候,会产生219+218+2^17次调用(二叉树最后三层个数计算)这就是占据了80%以上的函数调用,这时候,我们可以进行小区间优化,当区间个数小于10的时候采用InsertSort,大于10,采用快排。就可以了。

void QuickSort(int* arr, int begin, int end)
{
	if (begin >= end)
		return;

	//三数取中对有序数组进行优化
	int index = midnum(arr, begin, end);
	Swap(&arr[begin], &arr[index]);


	int left = begin;
	int right = end;
	int key = arr[begin];
	int pivot = begin;
	while(left < right)
	{
		while(left< right && arr[right] >= key)
		{
			--right;
		}
			arr[pivot] = arr[right];
			pivot = right;
		while(left < right && arr[left] <= key)
		{
			++left;
		}
			arr[pivot] = arr[left];
			pivot = left;
	}
	pivot = left;
	arr[pivot] = key;
	//小区间优化
	if ((pivot - 1 - begin+1) > 10)
	{
		QuickSort(arr, begin, pivot - 1);
	}
	else
	{
		InsertSort(arr + begin, pivot - 1 - begin + 1);
	}
	if ((end - pivot - 1 + 1) > 10)
	{
		QuickSort(arr, pivot + 1, end);
	}
	else
	{
		InsertSort(arr + pivot + 1, end - pivot - 1 + 1);
	}
	//QuickSort(arr, begin, pivot - 1);
	//QuickSort(arr, pivot + 1, end);
}

快速排序版本2:左右指针法

因为左右指针法和挖坑法,在第一次排完序之后各个数字的位置不同,在选择题中可能会遇到,所以这里我会都介绍一下的。能写出一种即可。


void QuickSort2(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	int left = begin;
	int right = end;
	int key = left;
	while (left < right)
	{
		while (left < right && arr[right] >= arr[key])
			right--;
		while (left < right && arr[left] <= arr[key])
			++left;
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[key], &arr[left]);

	QuickSort2(arr, begin, left - 1);
	QuickSort2(arr, left+1, end);
}

快速排序版本3:前后指针法

首先定义一个前指针prev,然后一个在后面的指针cur,cur先移动,去数组后面找小于arr[keyi]的值,找到了就++prev然后交换prev位置的值和cur位置的值,相当于是把小于key的值向前扔,大于key的值向后扔。最后cur走到数组尾部,prev指向最后一个小于key的值,将arr[keyi]于arr[prev]交换即可。

//前后指针法
void QuickSort3(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	//三数取中对有序数组进行优化
	int index = midnum(arr, begin, end);
	Swap(&arr[begin], &arr[index]);

	int keyi = begin;
	int prev = begin; 
	int cur = begin + 1;
	while (cur <= end)
	{
		while (arr[cur] < arr[keyi] && ++prev != cur)
		{
			Swap(&arr[cur], &arr[prev]);
		}
		++cur;
	}
	Swap(&arr[keyi], &arr[prev]);

	QuickSort3(arr, begin, prev - 1);
	QuickSort3(arr, prev + 1, end);
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 19:31:09  更:2022-08-19 19:33:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 12:25:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码