可能是你看过最全的十大排序算法详解(完整版代码)

目录
  • 前言
  • 交集排序
    • 冒泡
    • 简单
    • 快速排序
  • 插入排序
    • 直接插入排序
    • 希尔排序
  • 选择排序
    • 简单选择排序
    • 堆排序
  • 归并排序
    • 二路
    • 多路
  • 非比较类
    • 计数排序
    • 桶排序
    • 基数排序
  • 最后

前言

兄弟们,应上篇数据结构的各位要求,今天我开始工作了,开始肝算法,剑指offer还在路上,我真想开车去接它,奈何码神没有驾照的开车,算了,弄排序算法吧,有点长,耐心看啊,原创不易,你们懂的,先上一张图

可以看出排序算法,还是比较多的,算了,不多说了,你我肝完就是出门自带4年实习经验的!

交集排序

冒泡

冒泡我一般也将它称为枚举就是把所有都走一遍嘛,效率比较低,一般在算法竞赛中如果实在没有比较好的,可以用,那就先讲一个简单的枚举吧!

枚举字典序

首先可能有的同学不知道什么是字典序,请看:
(1,2,3),(1,3,2)…这就是字典序的体现,官方解释是这样的:

字典排序(lexicographical order)是一种对于随机变量形成序列的排序方法。其方法是,按照字母顺序,或者数字小大顺序,由小到大的形成序列。

比如说有一个随机变量X包含{1 2 3}三个数值。

其字典排序就是{1 2 3} {1 3 2} {2 1 3} {2 3 1} {3 1 2} {3 2 1}

如果是字母:先比较第一个字符i和b,b<i,b是第2个,i是第9个2<9于是baray<ilove如果第一位相同,就比较第二位,

例如:abcdd<abcde aaaay<aaaaz如果其中之一是另一个的前缀,则短的那个排前面:aaa

下面用代码实现一下1-n的排列:

//冒泡排序,我也将它称为枚举
#include<iostream>
#include<cstdio>
using namespace std;
void print(int n, int *a, int cur)
{
	if (cur == n)//递归边界
	{
		for (int i = 0; i < n; i++)
		{
			printf("%d", a[i]);
		}
		printf("\n");
	}
	else for (int i = 1; i <= n; i++)
	{
		int OK = 1;
		for (int j = 0; j < cur; j++)
		{
			if (a[j] == i)//判断i是否出现过
				OK = 0;
			if (OK)//i没有出现过下一个
			{
				a[cur] = i;
				print(n, a, cur + 1);//递归
			}
		}
	}
}
int main()
{

}

下面我们来看一下正宗的冒泡排序,总体思想是:俩俩比较,如果反序交换,直到没有反序的记录为止,代码实现比较简单,是俩个for循环的嵌套

#include<iostream>
#include<algorithm>//调用算法库,使用交换函数swap
#include<cstdio>
using namespace std;
int main()
{
	int a[10];
	for (int i = 0; i < 10; i++)
	{
		cin >> a[i];

	}
	for (int i = 0; i < 10; i++)
	{
		for (int j = i + 1; j < 10; j++)
		{
			if (a[i] < a[j])
				swap(a[i], a[j]);
		}
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

总体来说比较简单,但是耗时,耗内存,反正就是不好,来优化一下,为什么不好?归根结底还是做了许多重复的运算,大量的比较,
总体思路是这样的:再某一次比较后,发现所有的数据都变成了顺序,直接退出循环,不再继续循环

//将for改为
	bool flag = true;
	for (int i = 0; i < 10 && flag; i++)
	{
		flag = false;
		for (int j = 10 - 1; j >= i; j--)
		{
			if (a[j] > a[i])
			{
				swap(a[j], a[j + 1]);
					flag = true;
			}
		}
	}

算法复杂度:俩个for,就是O(n^2)了,有点大

简单

选择排序

先来和冒泡排序比较一下,他俩的主要区别就是冒泡排序的数据在不断的交换,而快速排序先交换数据的别名,再交换本身。打个比喻就是,一个是幻想天上掉馅饼,背水一战,的炒股短线选手,而另一个则是看中时机的炒股老手,俗称股神。

好了,比较也比较完了,我们来看简单的代码实现吧

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int main()
{
	int a[10];
	for (int i = 0; i < 10; i++)
	{
		cin >> a[i];
	}
	for (int i = 0; i < 10; i++)
	{
		int min = i;
		for (int j = i + 1; j < 10; j++)
		{
			if (a[min] > a[j])
				min = j;//交换下标位置
		}
		if (i != min)
			swap(a[i], a[min]);
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

如果来分析算法复杂度的话,你会惊讶的发现时间复杂度仍旧是O(n^2),但是我要说的是它仍旧优于冒泡排序,why?

冒泡排序和选择排序是排序算法中比较简单和容易实现的算法。冒泡排序的思想为:每一次排序过程,通过相邻元素的交换,将当前没有排好序中的最大(小)移到数组的最右(左)端。而选择排序的思想也很直观:每一次排序过程,我们获取当前没有排好序中的最大(小)的元素和数组最右(左)端的元素交换,循环这个过程即可实现对整个数组排序。

选择排序的平均时间复杂度比冒泡排序的稍低:

同样数据的情况下,2种算法的循环次数是一样的,但选择排序只有0到1次交换,而冒泡排序只有0到n次交换

快速排序

和冒泡排序相似,但是优于冒泡,总体是一个分治的思想,交换轴点元素

  1. 划分:将数组中的元素都重排分成左右俩部分,使得左边都小于等于右边的任意元素
  2. 递归求解:把左右分别进行排序
  3. 合并:这时你会发现已经排列好了

还是排列一串数字,进行代码实现:

#include<iostream>
using namespace std;

void quickSort(int *arr,int begin,int end)
{
//begin为左,end为右
	//如果区间不只一个数
	if(begin < end)
	{
		int temp = arr[begin]; //将区间的第一个数作为基准数
		int i = begin; //从左到右进行查找时的“指针”,指示当前左位置
		int j = end; //从右到左进行查找时的“指针”,指示当前右位置
		//不重复遍历
		while(i < j)
		{
			//当右边的数大于基准数时,略过,继续向左查找
			//不满足条件时跳出循环,此时的j对应的元素是小于基准元素的
			while(i<j && arr[j] > temp)
				j--;
			//将右边小于等于基准元素的数填入右边相应位置
			arr[i] = arr[j];
			//当左边的数小于等于基准数时,略过,继续向右查找
			//(重复的基准元素集合到左区间)
			//不满足条件时跳出循环,此时的i对应的元素是大于等于基准元素的
			while(i<j && arr[i] <= temp)
				i++;
			//将左边大于基准元素的数填入左边相应位置
			arr[j] = arr[i];
		}
		//将基准元素填入相应位置
		arr[i] = temp;
		//此时的i即为基准元素的位置
		//对基准元素的左边子区间进行相似的快速排序
		quickSort(arr,begin,i-1);
		//对基准元素的右边子区间进行相似的快速排序
		quickSort(arr,i+1,end);
	}
	//如果区间只有一个数,则返回
	else
		return;
}
int main()
{
	int num[12] = {23,45,17,11,13,89,72,26,3,17,11,13};
	int n = 12;
	quickSort(num,0,n-1);
	cout << "排序后的数组为:" << endl;
	for(int i=0;i<n;i++)
		cout << num[i] << ' ';
	cout << endl;
	return 0;
}

算一下复杂度吧,最坏O(n^2),平均O(nlogn)几乎没有最坏的情况发生,所以效率还是比较高的,想一想如果就只要最大的值怎么弄?

#include<iostream>
using namespace std;
int Partition(int *a, int i, int j)
{
	int tmp = a[j];
	int index = i;
	if (i < j)
	{
		for (int k = i; k < j; k++) {
			if (a[k] >= tmp) {
				swap(a[index++], a[k]);
			}
		}
		swap(a[index], a[j]);
		return index;
	}
}

int Search(int a[], int i, int j, int k)
{
	int m = Partition(a, i, j);
	if (k == m - i + 1) return a[m];
	else if (k < m - i + 1)
	{
		return Search(a, i, m - 1, k);
	}
	//后半段
	else
	{

		//核心后半段:再找第 k-(m-i+1)大的数就行
		return Search(a, m + 1, j, k - (m - i + 1));
	}
}
int main()
{
	int a[7] = { 8,7,6,1,2,3,4 };
	int k = 3;
	cout << Search(a, 2, 6, k);
}

插入排序

直接插入排序

话说,码神最近在玩斗地主,你们说手机斗地主和真人斗地主最大的区别,或者是说好处是什么?我感觉就是在手机上不用插牌了,省时间,这利用的就是插入排序的原理,可以说是“斗地主排序”

基本操作:将一个记录插入到已经排好的有序表中,从而得到一个新的,记录数据+1的有序表
基操,看代码:

void insertionSort(int *arr, int len) {
    if (len<2) {
        return ;
    }

    for (int i=1; i<len; i++) {
        int insertValue = arr[i];//暂存需要插入元素
        int j = i-1;

        //从右向左比较元素
        for (; j>=0 && insertValue<array[j]; j--) {
            arr[j+1] = arr[j];
        }

        arr[j+1] = insertValue;
    }
}

老规矩,分析时间复杂度,最好的情况是顺序都是排列好的,此时只需要比较,时间复杂度为O(n),最坏的情况为O(n^2),平均下来是n ^ 2/4,所以平均时间复杂度也是O(n ^ 2).

希尔排序

如果说谁是第一个将排序算法复杂度突破O(n^2)的,那么我想希尔是第一个,可以说希尔排序是对插入排序的改进,区别在于,希尔排序可以说是一个不断分组的排序

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;

按增量序列个数k,对序列进行k 趟排序;

每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

实现如下:

 //希尔排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap>1)
	{
		//每次对gap折半操作
		gap = gap / 2;
		//单趟排序
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tem = arr[end + gap];
			while (end >= 0)
			{
				if (tem < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tem;
		}
	}
}

时间复杂度,牛逼的人们,通过大量的计算发现是O(n^3/2),小于O
(n^2),由于是跳跃式的排序所以不是稳定排序

选择排序

简单选择排序

可参考上面

堆排序

何为堆,如果已经学过堆的话,就是那个堆,与栈相对的堆,

基本思路:将代排的序列构造成一个大堆,此时,整个序列的最大值就是堆顶的根结点,将它移走,也就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值,然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素的次最大值,如此递归反复就会得到一个有序序列

varlen;   // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量

function buildMaxHeap(arr) {  // 建立大顶堆
    len = arr.length;
    for(vari = Math.floor(len/2); i >= 0; i--) {
        heapify(arr, i);
    }
}

function heapify(arr, i) {    // 堆调整
    varleft = 2 * i + 1,
        right = 2 * i + 2,
        largest = i;

    if(left < len && arr[left] > arr[largest]) {
        largest = left;
    }

    if(right < len && arr[right] > arr[largest]) {
        largest = right;
    }

    if(largest != i) {
        swap(arr, i, largest);
        heapify(arr, largest);
    }
}      

function swap(arr, i, j) {
    vartemp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

function heapSort(arr) {
    buildMaxHeap(arr);

    for(vari = arr.length - 1; i > 0; i--) {
        swap(arr, 0, i);
        len--;
        heapify(arr, 0);
    }
    return arr;
}

时间可以说是主要耗在了初始建堆和在重建堆的反复筛选上

1.构造堆:O(n)

2.重建堆:完全二叉树:数据结构,详解时间复杂度为O(nlogn)

又因为堆排序对原始记录的排序状态并不敏感,因此它无论是好是坏,时间复杂度都为O(nlogn),同希尔排序,都为不稳定性的排序

归并排序

完全二叉树,是一棵神奇的树,可以说归并排序是完全体现了完全二叉树的性质

二路

若将两个有序表合并成一个有序表,称为2-路归并。

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。
#include<iostream>
using namespace std;
void Merge(int[], int, int[], int, int, int)
void MergeSort(int numbers[], int length, int temp[], int begin, int end)
{
	//1. 同样判断传入的参数是否有效
	if (numbers == nullptr || length <= 0 || begin < 0 || end >= length)
		throw new exception("Invalid input.");

	//2. 作为递归的结束条件,开始下标和结束下标相等时,说明子序列中只有一个元素,看作有序的
	if (end - begin == 0)
		return;

	//3. 定义中间变量,将数组分半【如果有7个元素,下标0-6,则middle=3,数组分为长度为4和3的两段】
	int middle = ((end - begin) / 2 ) + begin;
	//4. 递归,先递归左半边,再递归右半边,将左右子序列不断分为长度为1的子序列才停止递归
	MergeSort(numbers, length, temp, begin, middle);
	MergeSort(numbers, length, temp, middle + 1, end);
	//5. 再慢慢归并
	Merge(numbers, length, temp, begin, end, middle);
}

void Merge(int numbers[], int length, int temp[], int begin, int end, int middle)
{
	//1. 判断是否有不符合要求的参数传入,有则抛出错误
	if (numbers == nullptr || length <= 0 || begin < 0 || end >= length)
		throw new exception("Invalid input.");

	//2. 将原序列从中分开
	int leftIndex = begin;		//左边序列的开始(左边序列的结尾是middle)
	int rightIndex = middle + 1;//右边序列的开始(右边序列的结尾是end)
	int tempIndex = begin;		//辅助数组的下标
	//3. 当左右子序列尚未到头时,循环
	while (leftIndex <= middle && rightIndex <= end)
	{
		//4. 两两对比判断,谁大谁就放入辅助数组,同时指针后移
		if (numbers[leftIndex] < numbers[rightIndex])
			temp[tempIndex] = numbers[leftIndex++];
		else
			temp[tempIndex] = numbers[rightIndex++];
		//5. 辅助数组下标++
		++tempIndex;
	}

	//6. 当左边或右边子序列尚未到头时,直接放入辅助数组
	while (leftIndex <= middle)
		temp[tempIndex++] = numbers[leftIndex++];

	while (rightIndex <= end)
		temp[tempIndex++] = numbers[rightIndex++];

	//7. 再将辅助数组中已经有序的元素覆盖掉原数组中无序的元素,使原数组变成部分有序
	for (int i = begin; i <= end; ++i)
		numbers[i] = temp[i];
}

int main(int arvc, char* argv[])
{
	const int length = 9;
	int nums[length] = { 18, 7, 23, 3, 9, 32, 10 , 99, 0};
	int *temp = new int[length];

	MergeSort(nums, length, temp, 0, 8);

	for (int i = 0; i < length; i++)
		cout << nums[i] << "  ";

	delete[] temp;
	temp = nullptr;
	cout << endl;
	return 0;
}

多路

同理,将多个有序表合并,称为多路归并,和二路归并几乎一样,就不赘述了,

非比较类

计数排序

计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

  1. 找出待排序的数组中最大和最小的元素;
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加;
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
#include <iostream>
using namespace std;
const int MAXN = 1000;
int arr[MAXN];

void counting_sort(int n)
{
	int min_value = 0x3f3f3f3f, max_value = 0;
	for (int i = 0; i < n; i++)
	{
		if (arr[i] > max_value)
			max_value = arr[i];
		if (arr[i] < min_value)
			min_value = arr[i];
	}
	int len = max_value - min_value + 1;
	int* bucket = new int[len]();
	for (int i = 0; i < n; i++)
	{
		bucket[arr[i] - min_value]++;
	}
	for (int i = 0, j = 0; i < len; i++)
	{
		while (bucket[i] != 0)
		{
			arr[j++] = i + min_value;
			bucket[i]--;
		}
	}
	delete bucket;
}

int main()
{
	int n;
	cout << "请输入数组中元素的个数:";
	cin >> n;
	cout << "请输入元素: " << endl;
	for (int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	cout << "排序前:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	counting_sort(n);
	cout << "排序后:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	return 0;
}

时间复杂度是O(n+k),空间复杂度也是O(n+k),其排序速度快于任何比较排序算法。当k不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。

桶排序

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

  • 设置一个定量的数组当作空桶;
  • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
  • 对每个不是空的桶进行排序;
  • 从不是空的桶里把排好序的数据拼接起来。
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 1000;
const int BUCKET_SIZE = 10;//默认每个桶的范围
int arr[MAXN];

void insert_sort(vector<int> &v){
	int len=v.size(),temp,i,j;
	for(i=1;i<len;i++){
		temp = v[i];
		for(j=i;j>0 && v[j-1]>temp;j--){
			v[j]=v[j-1];
		}
		v[j]=temp;
	}
}

void bucket_sort(int n){
	int min_value = 0x3f3f3f3f, max_value = 0;
	for (int i = 0; i < n; i++)
	{
		if (arr[i] > max_value)
			max_value = arr[i];//获取输入数据的最大值
		if (arr[i] < min_value)
			min_value = arr[i];//获取输入数据的最小值
	}
	//桶的初始化
	int len = (max_value-min_value)/BUCKET_SIZE+1;
	vector<int> bucket[len];
	//将数据分配到桶
	for(int i=0;i<n;i++){
		bucket[(arr[i]-min_value)/BUCKET_SIZE].push_back(arr[i]);
	}
	for(int i=0,j=0;i<len;i++){
		//这里建议使用插入排序或者计数排序。当然也可以使用堆排序,快速排序等等
		insert_sort(bucket[i]);
		for(auto x:bucket[i]){
			arr[j++]=x;
		}
	}
}

int main(){
	int n;
	cout << "请输入数组中元素的个数:";
	cin >> n;
	cout << "请输入元素: " << endl;
	for (int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	cout << "排序前:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	bucket_sort(n);
	cout << "排序后:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	return 0;
}

桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。是一个用空间换时间的算法

基数排序

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

  • 取得数组中的最大数,并取得位数;
  • arr为原始数组,从最低位开始取每个位组成r数组;
  • 对r进行计数排序(利用计数排序适用于小范围数的特点);
#include <iostream>
#include <queue>
using namespace std;
using namespace std;
const int MAXN = 1000;
int arr[MAXN];
queue<int> q[10];

void radix_sort(int n)
{
	//获取最大值的位数
	int max_value = 0;
	for (int i = 0; i < n; i++)
	{
		if (max_value < arr[i])
			max_value = arr[i];
	}
	int max_digit = 0;
	while (max_value)
	{
		max_digit++;
		max_value /= 10;
	}
	//开始排序
	int mod = 10, dev = 1;
	for (int i = 0; i < max_digit; i++, mod *= 10, dev *= 10)
	{
		for (int j = 0; j < n; j++)
		{
			int ix = arr[j] % mod / dev;
			q[ix].push(arr[j]);
		}
		int pos = 0;
		for (int j = 0; j < 10; j++)
		{
			while (!q[j].empty())
			{
				arr[pos++] = q[j].front();
				q[j].pop();
			}
		}
	}
}

int main()
{
	int n;
	cout << "请输入数组中元素的个数:";
	cin >> n;
	cout << "请输入元素: " << endl;
	for (int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	cout << "排序前:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	radix_sort(n);
	cout << "排序后:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	return 0;
}

基数排序基于分别排序,分别收集,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2n) ,当然d要远远小于n,因此基本上还是线性级别的。基数排序的空间复杂度为O(n+k),其中k为桶的数量。一般来说n>>k,因此额外空间需要大概n个左右。

最后

到此这篇关于十大排序算法的文章就介绍到这了,更多相关十大排序算法详解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言实现排序算法之归并排序详解

    排序算法中的归并排序(Merge Sort)是利用"归并"技术来进行排序.归并是指将若干个已排序的子文件合并成一个有序的文件. 一.实现原理: 1.算法基本思路 设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中. (1)合并过程 合并过程中,设置i,j和p三个指针,其初值分别指向这三个记录区的起始位置.合并时依次比较R[i

  • c++中八大排序算法

    概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短:  1.插入排序-直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入

  • C++堆排序算法的实现方法

    本文实例讲述了C++实现堆排序算法的方法,相信对于大家学习数据结构与算法会起到一定的帮助作用.具体内容如下: 首先,由于堆排序算法说起来比较长,所以在这里单独讲一下.堆排序是一种树形选择排序方法,它的特点是:在排序过程中,将L[n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素. 一.堆的定义 堆的定义如下:n个关键字序列L[n]成为堆,当且仅当该序列满足: ①L(i) <= L(2i)且L(i) <= L(2

  • C++冒泡排序算法实例

    冒泡排序 大学学习数据结构与算法最开始的时候,就讲了冒泡排序:可见这个排序算法是多么的经典.冒泡排序是一种非常简单的排序算法,它重复地走访过要排序的数列,每一次比较两个数,按照升序或降序的规则,对比较的两个数进行交换.比如现在我要对以下数据进行排序: 10 3 8 0 6 9 2 当使用冒泡排序进行升序排序时,排序的步骤是这样的: 3 10 8 0 6 9 2  // 10和3进行对比,10>3,交换位置 3 8 10 0 6 9 2  // 10再和8进行对比,10>8,交换位置 3 8 0

  • c语言快速排序算法示例代码分享

    步骤为:1.从数列中挑出一个元素,称为 "基准"(pivot);2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边).在这个分区退出之后,该基准就处于数列的中间位置.这个称为分区(partition)操作.3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序.递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了.虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iterat

  • c++ 快速排序算法【过程图解】

    第一.算法描述 快速排序由C. A. R. Hoare在1962年提出,该算法是目前实践中使用最频繁,实用高效的最好排序算法, 快速排序算法是采用分治思想的算法,算法分三个步骤 1.从数组中抽出一个元素作为基数v(我们称之为划界元素),一般是取第一个.最后一个元素或中间的元素 2.将剩余的元素中小于v的移动到v的左边,将大于v元素移动到v的右边 3.对左右两个分区重复以上步骤直到所有元素都是有排序好. 第二.算法实现 /*序列划分函数*/ int partition(int a[], int p

  • C语言排序算法之插入排序

    算法实现: 使用插入排序将下面的数字按照从小到大的顺序排列 步骤1:数组中已经排好的是{1},将9插入数组中 步骤2:数组中已经排好的是{2,9},将5插入数组中 步骤3:数组中已经排好的是{2,5,9},将4插入数组中 步骤4:数组中已经排好的是{2,4,,5,9},将8插入数组中 步骤5:数组中已经排好的是{2,4,,5,8,9},将1插入数组中 步骤6:数组中已经排好的是{1,2,4,,5,8,9},将6插入数组中 步骤7:排序完成 程序代码: #include <stdio.h> #i

  • 可能是你看过最全的十大排序算法详解(完整版代码)

    目录 前言 交集排序 冒泡 简单 快速排序 插入排序 直接插入排序 希尔排序 选择排序 简单选择排序 堆排序 归并排序 二路 多路 非比较类 计数排序 桶排序 基数排序 最后 前言 兄弟们,应上篇数据结构的各位要求,今天我开始工作了,开始肝算法,剑指offer还在路上,我真想开车去接它,奈何码神没有驾照的开车,算了,弄排序算法吧,有点长,耐心看啊,原创不易,你们懂的,先上一张图 可以看出排序算法,还是比较多的,算了,不多说了,你我肝完就是出门自带4年实习经验的! 交集排序 冒泡 冒泡我一般也将它

  • VSCode插件开发全攻略之package.json详解

    package.json 在详细介绍vscode插件开发细节之前,这里我们先详细介绍一下vscode插件的package.json写法,但是建议先只需要随便看一下,了解个大概,等后面讲到具体细节的时候再回过头来看. 如下是package.json文件的常用配置,当然这里还不是全部: { // 插件的名字,应全部小写,不能有空格 "name": "vscode-plugin-demo", // 插件的友好显示名称,用于显示在应用市场,支持中文 "displa

  • Android 欢迎全屏图片详解及实例代码

    Android 欢迎全屏图片详解 其实欢迎界面就是在主Activity之前再添加一个欢迎的Activity.在这个Activity中实现欢迎界面,和其他的Activity用法 是基本一样,只有细微的差别.     1.在Activity的onCreate方法中实现: @Override ic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /**全屏设置,隐藏窗口所有装饰**/ getW

  • JS 全屏和退出全屏详解及实例代码

    JS 全屏和退出全屏 js实现浏览器窗口全屏和退出全屏的功能,市面上主流浏览器如:谷歌.火狐.360等都是兼容的,不过IE低版本有点瑕疵(全屏状态下仍有底部的状态栏). 这个demo基本是够了,直接复制下面的源码另存为html文件看效果吧. <!DOCTYPE html> <html> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <

  • SqlServer 英文单词全字匹配详解及实现代码

    SqlServer英文单词全字匹配 环境:Vs2013+Sql Server2012 问题:现在数据库记录如下: Sentence列保存的是英文的句子,我现在想找出所有包含"I"(单词)的句子,如果我用 Sentence like '%I',作为条件的话,那么像上图选中的那条有个单词"it"(不区分大小写的情况下),它也会被Select出来,而我只想找出含有"I"这个单词的句子的记录. 解决:SqlServer提供了模式匹配,类似于正则,详细内容

  • 通过字节码看java中this的隐式传参详解

    前言 从字节码看java中 this 隐式传参具体体现(和python中的self如出一辙,但是比python中藏得更深),也发现了 static 与 非 static 方法的区别所在! static与非static方法都是存储java的方法区.在static 方法中,没有this引用,因此无法使用当前类中所定义的变量,而非static方法则会默认传入this. 概述 this关键字,是一个隐式参数,另外一个隐式参数是super. this用于方法里面,用于方法外面无意义. this关键字一般用

  • mongodb增量/全量备份脚本的实现详解

    前言 mongodb备份脚本,可以全量或增量进行备份,两年前所写,目前网上mongodb备份相关的脚本也还是很少.下面话不多说了,来一起看看详细的介绍吧 功能 定期对mongodb数据库数据进行全量备份或增量备份(副本集架构),并可以压缩上传到阿里云oss(本地会先生成压缩包,可以设置不上传oss). 脚本运行环境 使用python语言编写,需安装python,pymongo和mongodb shell客户端(测试时使用python 2.7.6,pymongo 3.0.3和mongodb she

  • Python利用全连接神经网络求解MNIST问题详解

    本文实例讲述了Python利用全连接神经网络求解MNIST问题.分享给大家供大家参考,具体如下: 1.单隐藏层神经网络 人类的神经元在树突接受刺激信息后,经过细胞体处理,判断如果达到阈值,则将信息传递给下一个神经元或输出.类似地,神经元模型在输入层输入特征值x之后,与权重w相乘求和再加上b,经过激活函数判断后传递给下一层隐藏层或输出层. 单神经元的模型只有一个求和节点(如左下图所示).全连接神经网络(Full Connected Networks)如右下图所示,中间层有多个神经元,并且每层的每个

  • 从Linux源码看Socket(TCP)Client端的Connect的示例详解

    前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看下Client端的Socket在进行Connect的时候到底做了哪些事情.由于篇幅原因,关于Server端的Accept源码讲解留给下一篇博客. (基于Linux 3.10内核) 一个最简单的Connect例子 int clientSocket; if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

  • 关于正则表达式基本语法的应用详解(必看篇)

    1.正则表达式基本语法 两个特殊的符号'^'和'$'.他们的作用是分别指出一个字符串的开始和结束.例子如下: "^The":表示所有以"The"开始的字符串("There","The cat"等): "of despair$":表示所以以"of despair"结尾的字符串: "^abc$":表示开始和结尾都是"abc"的字符串--呵呵,只有&qu

随机推荐