【数据结构】快排的详细讲解

江河入海,知识涌动,这是我参与江海计划的第7篇。

目录:

介绍

一,递归快排确定基准值

二,递归遍历

三,非递归的快排

四,快排的效率


介绍

        快排是排序算法中效率是比较高的,快排的基本思想是运用二分思想,与二叉树的前序遍历类似,将数据划分,每次划分确定1个基准值(就是已经确定好有序后位置的数据),以升序为例,基准值左面的数据都比此值小,右面的数据都比此值大,然后以基准值为分界线,经过不断划分,最终基准值的个数达到n,数据即有序,因此,递归运用是不二选法,也可运用非递归,但是比较麻烦。

一,递归快排确定基准值

确定基准值的方法常用的有三种,普通法,挖坑法,前后指针法。

1,普通法,具体思想图如下(以升序为例):


        上面说过,基准值的确定过程要保证左面的数据都比此值小或大,右面的数据都要比此值大或小,因此,此基准值就确定了在整个数据中的位置。以升序为例,我们可以从开头和末尾遍历数据,比开头(以首元素为基准)元素大的放在最后,比开头元素小的数据放在前面,最终当两者相遇后再与开头元素交换即可确定基准值(注意:此步骤有细节,具体后面会说明)。如下图:

基准值过程图

        现在有个注意要素套提一下,当上图中的前面L和后面R相遇时,如何保证此值一定比首元素小呢?这里我们需要控制好L的走向即可,即让R先走,当遇见比首元素小时退出,然后让L走,最后让两者进行交换,这样一来无论出现什么情况,当L与R相遇时对应的数据将一定比首元素(即以第一个元素为基准)小,此步骤称为预排序。

基准值的确定代码如下:

void Swap(int* x1, int* x2) {
    int t = *x1;
    *x1 = *x2;
    *x2 = t;
}

int PartSort1(int* a, int begin, int end) {
    int key = begin;
    while (begin < end) {
        //注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
        //因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.

        while (begin < end && a[end] >= a[key]) {
            end--;
        }
        //当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
        while (begin < end && a[begin] <= a[key]) {
            begin++;
        }
        Swap(a + end, a + begin);
    }
    Swap(a + key, a + begin);
    return begin;
}


2,挖坑法,原理图如下(以升序为例):

        挖坑发大致思想与普通法一样,不同的是挖坑发有了坑位。挖坑发是先将首元素保存,将此位置形成坑位(其实坑位上有数据,但坑位的数据不影响,为了方便理解,所以在上图中的坑位就没写上去),然后开始首尾遍历(尾要先遍历,原理同上),比key大的元素放在后面,比key小的元素放在前面,一旦不满足此情况,这个数据将给到位置L或位置R,原本的位置将会形成坑位,直到两者相遇为止,结束遍历,最后把key的值放入坑位即可。代码如下:

int PartSort2(int* a, int begin, int end) {
    int key = a[begin];
    int hole = begin;//开头先形成坑位
    while (begin < end) {
        // 右边先走(原理与PartSort1原理一样),找小,填到左边的坑,右边形成新的坑位
        while (begin < end && a[end] >= key) {
            end--;
        }
        a[hole] = a[end];
        hole = end;
        // 左边再走,找大,填到右边的坑,左边形成新的坑位
        while (begin < end && a[begin] <= key) {
            begin++;
        }
        a[hole] = a[begin];
        hole = begin;
    }
    a[hole] = key;
    return hole;
}


3,前后指针法(prev是前指针,cur是后指针,此指针是位置指针,不是我们所常说的指针型),原理图如下:

        前后指针法跟上面两种方法有很大不同,如上,以第一个元素为基准,即定义key值为首元素,cur往前遍历,prev随之跟上cur的步伐,当prev遇到的数据比key小,prve向前移动;当prev遇到的数据比key大,prev停止移动,此时,cur不断向前移动,一旦找到比key小的数据就会跟prev指向的数据进行交换,最后,当cur遍历完整个数据后cur与key会进行交换,确定此时key所对应的值比左边数据大,比右边数据小。代码如下:

void Swap(int* x1, int* x2) {
    int t = *x1;
    *x1 = *x2;
    *x2 = t;
}
int PartSort3(int* a, int begin, int end) {
    int front = begin + 1;
    int back = begin;
    while (front <= end) {
        if (a[front] <= a[begin]) {
            back++;
            Swap(a + back, a + front);
        }
        front++;
    }
    //因为后指针控制,所以当程序结束后back所指向的数据都比keyi所指向的数据小
    Swap(a + begin, a + back);
    return back;
}


总:以上三种遍历确定基准值的方法在快排称为预排序,每一趟预排序都可确定数据中一个元素的排序位置,每当确定一个数据后相对位置后,我们只需要不断以上次遍历时确定的基准值为界,递归遍历数据,即可确定最终确定序列。


二,递归遍历

        当我们明白如何确定基准值后,接下来就是程序的结构搭建了,上面说过,快排递归跟二叉树的前序遍历一样,并且还需要以基准值为分界线,不断确定基准值,具体思路导图如下:

        当确定好基准值key后,以区间[begin, key - 1]和区间[key + 1, end]进行划分(begin是要进行遍历时,开头元素的坐标,end是要遍历时,结尾元素的坐标,如上图),以次区间不断进行与二叉树前序遍历相同的递归,根据上图所示,很明显,当begin>=end时结束下一层递归。代码如下:

void QuickSort(int* a, int begin, int end)
{
    //即当不存在区间时结束,即就排好了一个数
    if (begin >= end)
        return;
    //运用普通法PartSort1,此算法是返回一个顺序表中中间值的坐标,在坐标左边都小于此数,在坐标的右边都大于此数
    int keyi = PartSort1(a, begin, end);//也可用挖坑法和前后指针法
    // 区间递归: 以keyi为界,左[begin, keyi-1],右[keyi+1, end],一直缩小,最终会逐渐会缩小成有序
    QuickSort(a, begin, keyi - 1);//在keyi的左面进行遍历
    QuickSort(a, keyi + 1, end);//在keyi的右面进行遍历
}

下面是总代码:

#include <stdio.h>
void Swap(int* x1, int* x2) {
	int t = *x1;
	*x1 = *x2;
	*x2 = t;
}
int PartSort1(int* a, int begin, int end) {
	int key = begin;
	while (begin < end) {
		//注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
		//因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.
		while (begin < end && a[end] >= a[key]) {
			end--;
		}
		//当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
		while (begin < end && a[begin] <= a[key]) {
			begin++;
		}
		Swap(a + end, a + begin);
	}
	Swap(a + key, a + begin);
	return begin;
}
void QuickSort(int* a, int begin, int end)
{
	//即当不存在区间时结束,即就排好了一个数
	if (begin >= end)
		return;
	//PartSort1算法是返回一个顺序表中中间值的坐标,在坐标左边都小于此数,在坐标的右边都大于此数
	int keyi = PartSort1(a, begin, end);
	// 区间递归: 左[begin, keyi-1] keyi 右[keyi+1, end],一直缩小,最终会逐渐会缩小成排序
	QuickSort(a, begin, keyi - 1);//在keyi的左面进行遍历
	QuickSort(a, keyi + 1, end);//在keyi的右面进行遍历
}
void Print(int* a, int n) {
	for (int i = 0; i < n; i++) {
		fprintf(stdout, "%d ", a[i]);
	}
	puts("");
}
void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	Print(a, sizeof(a) / sizeof(int));
}
int main() {
	TestQuickSort();
	return 0;
}

运行图:


三,非递归的快排

        运用非递归,大多数要运用栈结构,因为递归本身其实就是不断入栈和出栈,递归过程跟栈结构一样,进入递归就是入栈,出函数就是出栈,都是先进后出。快排的非递归实现,我们也可用栈来实现。根据前面递归的运用,递归是不断进行区间分割,我们可将此区间放入栈中,然后进行不断循环遍历,每当遍历时就将区间放入栈中,一旦用完此区间就释放,因为此区间我们已经遍历完了,释放此区间后是为了后面的区间遍历,跟递归中函数栈帧的创建与销毁一样。非递归结构代码如下:

1,栈的建立

typedef struct stack {
    int* Data;
    int Capacity;
    int Top;
}Stack;
 //以下三个是要运用栈结构的算法
void StackInit(Stack* S);
void StackPop(Stack* S);
void StackPush(Stack* S, int X);
//栈功能的实现
void StackInit(Stack* S) {//初始化栈
    assert(S);
    S->Data = 0;
    S->Capacity = 0;
    S->Top = -1;
}
void StackPop(Stack* S) {//出栈
    assert(S || S->Data || S->Top != -1);
    S->Top--;
}
void StackPush(Stack* S, int X) {//入栈
    assert(S);
    if (!S->Data) {
        S->Data = (int*)malloc(sizeof(int) * 4);
        assert(S->Data);
        S->Capacity = 4;
    }
    else if (S->Top == S->Capacity - 1) {
        S->Data = (int*)realloc(S->Data, (sizeof(int) * S->Capacity) * 2);
        assert(S->Data);
        S->Capacity *= 2;
    }
    S->Data[++S->Top] = X;
}

2,非递归的结构

void QuickSort(int* a, int left, int right) {
 //创建栈结构S,以栈来模仿递归过程
    Stack* S = (Stack*)malloc(sizeof(Stack));
    StackInit(S);
    StackPush(S, right);
    StackPush(S, left);
    while (S->Top != -1) {
        //确定左右区间,每当遍历完一次时要及时更换,即从栈中去除操作
        int begin = S->Data[S->Top];
        StackPop(S);
        int end = S->Data[S->Top];
        StackPop(S);
        //用指定好的区间进行预排序,即一次遍历
        int key = PartSort1(a, begin, end);
        //进行左区间的遍历
        if (end - 1 > begin) {
        //注意栈结构先进后出的特点,要先把end装进去
            StackPush(S, end - 1);
            StackPush(S, begin);
        }
        //进行右区间的遍历
        if (begin + 1 < end) {
        //同理,要先把end装进去
            StackPush(S, end);
            StackPush(S, begin + 1);
        }
    }
    free(S);
    //注意,不能在此算法内这样写,因为这是的a是首元素地址,即指针,sizeof(a)为地址的大小
    //Print(a, sizeof(a) / sizeof(int));

}

        以上是非递归过程中的逻辑代码,除此两大步,其它的逻辑运用与递归无任何区别,总代码如下:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef struct stack {
	int* Data;
	int Capacity;
	int Top;
}Stack;
 //以下三个是要运用栈结构的算法
void StackInit(Stack* S);
void StackPop(Stack* S);
void StackPush(Stack* S, int X);
//栈功能的实现
void StackInit(Stack* S) {//初始化栈
	assert(S);
	S->Data = 0;
	S->Capacity = 0;
	S->Top = -1;
}
void StackPop(Stack* S) {//出栈
	assert(S || S->Data || S->Top != -1);
	S->Top--;
}
void StackPush(Stack* S, int X) {//入栈
	assert(S);
	if (!S->Data) {
		S->Data = (int*)malloc(sizeof(int) * 4);
		assert(S->Data);
		S->Capacity = 4;
	}
	else if (S->Top == S->Capacity - 1) {
		S->Data = (int*)realloc(S->Data, (sizeof(int) * S->Capacity) * 2);
		assert(S->Data);
		S->Capacity *= 2;
	}
	S->Data[++S->Top] = X;
}
void Swap(int* x1, int* x2) {
	int t = *x1;
	*x1 = *x2;
	*x2 = t;
}
int PartSort1(int* a, int begin, int end) {
	int key = begin;
	while (begin < end) {
		//注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
		//因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.
		while (begin < end && a[end] >= a[key]) {
			end--;
		}
		//当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
		while (begin < end && a[begin] <= a[key]) {
			begin++;
		}
		Swap(a + end, a + begin);
	}
	Swap(a + key, a + begin);
	return begin;
}
void QuickSort(int* a, int left, int right) {
 //创建栈结构S,以栈来模仿递归过程
	Stack* S = (Stack*)malloc(sizeof(Stack));
	StackInit(S);
	StackPush(S, right);
	StackPush(S, left);
	while (S->Top != -1) {
		//确定左右区间,每当遍历完一次时要及时更换,即从栈中去除操作
		int begin = S->Data[S->Top];
		StackPop(S);
		int end = S->Data[S->Top];
		StackPop(S);
		//用指定好的区间进行预排序,即一次遍历
		int key = PartSort1(a, begin, end);
		//进行左区间的遍历
		if (end - 1 > begin) {
		//注意栈结构先进后出的特点,要先把end装进去
			StackPush(S, end - 1);
			StackPush(S, begin);
		}
		//进行右区间的遍历
		if (begin + 1 < end) {
		//同理,要先把end装进去
			StackPush(S, end);
			StackPush(S, begin + 1);
		}
	}
	free(S);
	//注意,不能在此算法内这样写,因为这是的a是首元素地址,即指针,sizeof(a)为地址的大小
	//Print(a, sizeof(a) / sizeof(int));
}
void Print(int* a, int n) {
	for (int i = 0; i < n; i++) {
		fprintf(stdout, "%d ", a[i]);
	}
	puts("");
}
int main() {
	int a[] = { 0,5,7,9,3,4,1,6,2,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	Print(a, sizeof(a) / sizeof(int));
	return 0;
}

运行图:

分析:

        此非递归的运用是每次通过基准值来确定的,当确定好一个基准值时就将此基准值的左右要遍历的区间加入栈中,因为无法保证每次进栈的区间跟递归程序一样,所以我们以基准为界,将左右区间加入,一直往下遍历,当区间缩小到一定的返回后就停止加入,最后再遍历栈中的区间即可。


四,快排的效率

1,快排的效率分析

        快排效率在平常说是效率比较高的,大致根据二叉树原理计算,快排时间复杂度为O(nlogn),空间复杂度为O(logn),但这只是对于大多时候,其实快排的时间效率是很不确定的,快排的效率跟数据的原有序列有关,序列越接近有序,快排效率越低。我们先观察以下图:

快排效率最好情况


快排效率最坏情况

        可知,快排预排序的时间效率在区间[logn,n],当原有序列越有序时,无论是递归还是非递归时间效率都很低,有序时效率最低,而遍历元素的时间复杂度不变,一直是O(n),因此快排的时间效率在区间[nlogn,n^2]。


2,三数取中

        当快排在有序时(升序为例),数据会靠近左边进行排序,而要想提高快排的效率,就必须尽量让基准值尽量往中间靠拢,但这样很难控制,因为这与数据原有的序列有关。虽然说我们不能直接控制,但是我们可控制最坏情况来进而控制时间效率,即序列有序时的情况。

        通常,我们是选取首元素为基准值的,因此,只要控制好首元素不为基准值的情况即可,也就是三数取中。

        三数取中是将判断首元素,尾元素,中间元素三者之间的大小,将中间大的数据与首元素交换,使首元素不可能为基准值。代码如下:

int GetMidi(int* a, int left, int right)
{
    int mid = (left + right) / 2;//中间数mid
    //下面比较 left mid right 三者之间大小,将中间大的数据的下标返回过去
    //先人left与mid比较,然后进一步判断right

    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
        {
            return mid;
        }
        else if (a[left] > a[right])  // mid是最大值
        {
            return left;
        }
        else
        {
            return right;
        }
    }
    else
    {
        if (a[mid] > a[right])
        {
            return mid;
        }
        else if (a[left] < a[right]) // mid是最小
        {
            return left;
        }
        else
        {
            return right;
        }
    }
}

        具体思想就是先两两比较,然后进一步与第三者比较,上面代码中选举了left与mid两者之间比较,然后再跟第三者right比较,满足中间大的数据返回其下标。


3,改善后算法的运用

        有了三数取中,快排将不会出现最坏情况,虽说有可能会出现次坏情况,但基本是不可能的,因为这种情况很是要求原序列的次序和三数取中的交换,因次,如若在算法中加上三数取中后算法的时间复杂度基本为O(nlogn)。下面是改进后运用的代码:

int GetMidi(int* a, int left, int right)
{
    int mid = (left + right) / 2;//中间数mid
    //下面比较 left mid right 三者之间大小,将中间大的数据的下标返回过去
    //先人left与mid比较,然后进一步判断right

    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
        {
            return mid;
        }
        else if (a[left] > a[right])  // mid是最大值
        {
            return left;
        }
        else
        {
            return right;
        }
    }
    else
    {
        if (a[mid] > a[right])
        {
            return mid;
        }
        else if (a[left] < a[right]) // mid是最小
        {
            return left;
        }
        else
        {
            return right;
        }
    }
}
void Swap(int* x1, int* x2) {
    int t = *x1;
    *x1 = *x2;
    *x2 = t;
}
//普通法
int PartSort1(int* a, int begin, int end) {
    //运用三数取中,与每次预排序的区间首元素交换,防止出现最坏情况
    int midi = GetMidi(a, begin, end);
    Swap(a + begin, a + midi);
    //以下代码正常不变
    int key = begin;
    while (begin < end) {
        //注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
        //因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.

        while (begin < end && a[end] >= a[key]) {
            end--;
        }
        //当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
        while (begin < end && a[begin] <= a[key]) {
            begin++;
        }
        Swap(a + end, a + begin);
    }
    Swap(a + key, a + begin);
    return begin;
}
//挖坑发
int PartSort2(int* a, int begin, int end) {
    //运用三数取中,与每次预排序的区间首元素交换,防止出现最坏情况
    int midi = GetMidi(a, begin, end);
    Swap(a + begin, a + midi);
    //以下代码正常不变
    int key = a[begin];
    int hole = begin;//开头先形成坑位
    while (begin < end) {
        // 右边先走(原理与PartSort1原理一样),找小,填到左边的坑,右边形成新的坑位
        while (begin < end && a[end] >= key) {
            end--;
        }
        a[hole] = a[end];
        hole = end;
        // 左边再走,找大,填到右边的坑,左边形成新的坑位
        while (begin < end && a[begin] <= key) {
            begin++;
        }
        a[hole] = a[begin];
        hole = begin;
    }
    a[hole] = key;
    return hole;
}
//前后指针法
int PartSort3(int* a, int begin, int end) {
    //运用三数取中,与每次预排序的区间首元素交换,防止出现最坏情况
    int midi = GetMidi(a, begin, end);
    Swap(a + begin, a + midi);
    //以下代码正常不变
    int front = begin + 1;
    int back = begin;
    while (front <= end) {
        if (a[front] <= a[begin]) {
            back++;
            Swap(a + back, a + front);
        }
        front++;
    }
    //因为后指针控制,所以当程序结束后back所指向的数据都比keyi所指向的数据小
    Swap(a + begin, a + back);
    return back;
}

        在以上中,除了预排序算法需要改进,其它的都不用动即可实现高效的快排。最后,跟大家再次强调一下快排的效率,快排在大多数情况下确实效率很高,但快排的效率受原数据的序列影响比较大,当序列越接近有序时,快排的效率可能还没有其它算法高,在以后的运用中要不要用快排还需根据原数据的情况而定。

青春:一叶知秋
关注 关注
  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
数据结构】——快排详解
qq_43412060的博客
02-18 3187
上一篇文章我们介绍了八大排序中的七种,今天这篇文章主要来详细介绍一种比较重要也是常用的一种排序算法——快速排序~ 一、什么是快排快速排序是一种二叉树结构的交换排序方法。相当于是冒泡排序的一种升级,都是属于交换排序类,通过不断比较和移动交换来实现排序。 其基本思想是:任意取待排序元素序列中的某个元素作为基准值,按照这个排序码把待排序集合分割成两个子序列,左子序列中所有元素都小于该基准值,右子序列...
Python 超详细算法数据结构视频教程
最新发布
05-06
课程的目录结构如下,每一章都有配套的文字讲义(markdown),示例代码,视频讲解详细讲解一般会放在视频里,使用手写板来 进行板书,包括文字、图示、手动模拟算法过程等。 课程介绍 课程简介之笨方法学算法 ...
数据结构——快速排序
Alan的博客
05-11 6196
1、排序思路 快速排序(quick sort)是由冒泡排序改进而得得,它的基本思想是在待排序的n个元素中n个元素中任取一个元素(通常取第一个元素)作为基准,把该元素放入适当的位置后,数据序列被此元素划分成两部分。所有关键字比该元素关键字小的放置在前一部分,所有关键字比该元素关键字大的放置在后一部分,并把该元素排在这两部分中间(称该元素归为),这个过程称为一趟快速排序,即一趟划分。   之后对产生的...
数据结构 排序总结
weixin_62558615的博客
12-28 552
排序总结大全
快速排序:最好,最坏以及平均复杂度推导理解
咕噜咕噜
05-25 2万+
算法简介: 快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。 步骤为: 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot), 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成, 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。 递归到最底部的判断条件是
快排算法详解快速排序详解多图详解
m0_57296700的博客
03-19 9419
1.快排的基础原理: 给定一个数组arr和一个数num,请把小于num的数放左边,等于num的数放中间,大于num的数放右边,要求:时间复杂度O(N),额外时间复杂度为O(1) 方法:可以先想一下数组经过这样的排序【这里的排序只是指把小于num的数放左边,等于num的数放中间,大于num的数放右边,而在小于num的数的范围里并没有固定的次序,大于也一样】 声明一个变量i,i++,一个变量less 总共就有三种情况: arr[i]< ...
快速排序最好,最坏,平均复杂度分析
热门推荐
weshjiness的专栏
03-11 12万+
转自http://book.51cto.com/art/201108/287089.htm 很好的一篇详细数学分析快排复杂度的文章~ 我们来分析一下快速排序法的性能。快速排序的时间性能取决于快速排序递归的深度,可以用递归树来描述递归算法的执行情况。如图9‐9‐7所示,它是{50,10,90,30, 70,40,80,60,20}在快速排序过程中的递归过程。由于我们的第一个关键字是50,正
数据结构快排
weixin_45963260的博客
02-14 273
快排的理解: 首先快排的作用是将一串数列排序,比冒泡和选择好的地方在于时间复杂度小->O(nlogn) 空间复杂度小,但是缺点是不稳定,如果原数列比较有序,那么该算法的优势就不是很大。 基本的原理是先确定一个基准数,同时有两个指针low,high分别指向第一位和最后一位, 然后从high开始往第一位走,找到一个比基准数小的数字,将基准数换成该数字,然后用 temp记录下来基准数,同时low往最后一位走,找到一个比high大的数,然后替换high,然后 high往前走找到一个比lo...
数据结构快排
lou_ym的专栏
05-28 451
结构体快速排序
数据结构-快排算法(详解)
m0_65835264的博客
08-06 707
在面试中常考的快排算法,用到了分而治之的思想,通过代码和实例进行了详细的解释,希望有所收获。
C语言算法快排+图+二叉树实现
02-22
图的遍历是数据结构里经典中的经典 解二叉树遍历的四种方式,分别为前序遍历、中序遍历、后续遍历和层序遍历。在学习遍历之前,会先带大家回顾一下二叉树的基本概念。学习二叉树的基本操作前,需要先创建一颗二叉树...
leetcode530-Play-with-Algorithms:基本的算法数据结构
06-30
基本的算法数据结构 来自liuyubobobo老师的课程 章节 讲解例题 课程练习题 更多扩展练习 难题推荐 第一章 算法面试到底是什么鬼? [无] [无] 第二章 面试中的复杂度分析 [无] [无] 第三章 数组中的问题最常见 3-1 ...
完整视频-coursera公开课 普林斯顿算法 ⅠⅡ部分
01-17
视频一个两部分,算法(一)主要集中在基础的数据结构、排序、查找算法。 相关主题有:并查集算法,二分查找,栈,队列,背包,插入排序,选择排序,希尔排序,快速排序, 三切分快排,归并排序,堆排序,二分堆,二...
快排的最好最坏情况
HC_wood的博客
08-17 5270
快排的最好最坏情况
快排
系统运维
09-12 93
void quicksort(int num[],int start,int end) { int key=num[start]; int prev = start; int last = end; //结束条件必不可少,否则将会进入死循环。 if(prev &gt;= last) { return; } while(prev &lt; last) { ...
数据结构详细讲解王道
09-27
数据结构是计算机科学中非常重要的一个概念,它是指数据对象以及它们之间的关系和操作。数据结构可以分为三类:线性结构、树形结构和图形结构。下面我会简要介绍这三类数据结构。 1. 线性结构:线性结构是最简单的...

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 【Linux系统编程】环境变量 1806
  • 基本的五大排序算法 1733
  • C/C++数据在计算机内存中的存储形式详解 1683
  • 【C++】容器适配器结构的设计 1439
  • 【C++】关联式容器 1403

最新评论

  • 【C++】哈希思想

    南木元元: 内容干货很多,学到了,支持~

  • 【C++】哈希思想

    普通网友: 干货满满,实用性强,博主的写作风格简洁明了,让人一目了然。文章涵盖了很多实用的知识点。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】

  • 【C++】哈希封装map与set

    林戈的IT生涯: 笔落半寸惊风雨,技法文成泣鬼神。 林戈捋过不忍去,留评四句作余音。

  • 【C++】哈希结构

    _BugMan: 【C++】哈希结构,赞一个喃

  • 【C++】哈希封装map与set

    屿小夏: 博主讲解的很详细,结构严谨。感谢博主分享,期待博主持续输出好文,同时也希望可以来我博客指导我一番!

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • 【C++】智能指针
  • 【C++】异常
  • 【C++11】lambda匿名函数和包装器
2024年29篇
2023年55篇

目录

目录

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

深圳SEO优化公司湘西至尊标王哪家好延安网站改版公司铜仁网页制作多少钱黑河高端网站设计推荐龙岗网站排名优化价格乌海网站优化按天收费多少钱常州网页制作报价黔东南企业网站建设多少钱菏泽网站关键词优化多少钱周口网站制作江门网络营销价格襄阳网站建设桐城模板制作报价周口网站制作设计多少钱哈尔滨网络推广公司孝感SEO按效果付费价格永州网站开发温州模板网站建设诸城营销网站价格呼和浩特关键词按天收费多少钱龙岩营销型网站建设价格潜江百度竞价价格芜湖seo报价苏州外贸网站建设多少钱汉中关键词按天收费秦皇岛建站多少钱舟山网站搭建推荐黄南至尊标王多少钱普洱品牌网站设计公司白山营销型网站建设公司歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化