【数据结构】快速排序

发布于:2022-12-31 ⋅ 阅读:(190) ⋅ 点赞:(0)

1.引入

快速排序的基本思想其实就是交换排序;我们所了解的冒泡排序就是一种交换排序;但是冒泡排序的时间复杂度为O(n^2),并不实用,而快速排序就是一种非常高效的排序,稳定性也比较不错,时间复杂度为O(n * lng n);

先来看一下冒泡排序,

void Bubble(int* a,int n)
{
    int i, j;
    for(i = 0; i < n - 1; i++)
    {
        for(j = 0; j < n - 1- i; j++)
        {
            if(a[j] > a[j + 1])
            {
                int tmp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = tmp;
            }
        }
    }
}

显然 这种交换排序的主要特点就是将值较大的记录向序列的尾部移动,值较小的记录向序列的前部移动;(升序,降序反之)。

先分析单趟的排序;单躺的冒泡排序是将 最大的值放到数组的最后面,之后就不考虑这个最大的数了,而快速排序的单趟是将准备好的 key 值放到 适当的位置(排好序的位置);

下面先来分析一下快速排序的单趟排序:

用两个参数 left 与 right ,分别从数组的两边出发,right 找比 key 值小的,找到之后停下,left 找比 key 值大的,找到之后也停下,互换两个数的,循环 直到 left == right;停下再将 key 与 left 互换;这样就是一个单趟的排序;将 key 值放到了适当的位置。 

int QuickSort_along(int* a,int left;int right)
{
    int keyi = left;
    while(left < right)
    {
        // R 先走 找小  L再找大(只找大小)
        while ((a[right] >= a[keyi]) && (left < right))
        {
            right--;
        }
        while ((a[left] <= a[keyi]) && (left < right))
        {
            left++;
        }
        if(left < right)
        swap(&a[left],&a[right]);
    }
    swap(&a[left],&a[keyi]);
    return left;
}

排完单趟的快速排序,现在来看整体的;

#include<stdio.h>
QuickSort(int* a,int left,int right)
{
    if(left < right)
    {
        return;
    }
    int keyi = QuickSort_along(a,left,right);
    QuickSort(a,left,keyi - 1);
    QuickSort(a,keyi + 1,right);
}
int main()
{
    int a[] = {10,9,8,7,6,5,4,3,2,1};
    QuickSort(a,0,9);
}

上面的方法其实是有缺陷的,如果当数据恰好是按照降序排的,又恰巧数据很大,这时就会发生递归过深,栈溢出的现象;我们可以有两个优化的方法,

1.三数取中:

int GetMidIndex(int* a, int left, int right)
{
	int mid = left + (right - left) / 2;
	if (a[left] > a[right])
	{
		if (a[left] > a[mid])
		{
			if (a[mid] > a[right])
				return mid;
			else
				return right;
		}
		else
			return left;
	}
	else
	{
		if (a[right] > a[mid])
		{
			if (a[mid] > a[left])
				return mid;
			else
				return left;
		}
		else
			return right;
	}
}

int QuickSort_along(int* a,int left;int right)
{
    int mid = GetMidIndex(a, left, right);
	swap(&a[mid], &a[left]);
    int keyi = left;
    while(left < right)
    {
        // R 先走 找小  L再找大(只找大小)
        while ((a[right] >= a[keyi]) && (left < right))
        {
            right--;
        }
        while ((a[left] <= a[keyi]) && (left < right))
        {
            left++;
        }
        if(left < right)
        swap(&a[left],&a[right]);
    }
    swap(&a[left],&a[keyi]);
    return left;
}

2.最小区间优化:

我们直到,当快速排序的递归模式和二叉树很像,递归越到深层,函数的调用就越多;而且调用的越深所排的数组就越有序,此时,我们跳出来,不让他进行快排的递归了,我们直接进行插入排序或者其他的排序来解决这一个问题。

void InserSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];
				--end;
			}
			else
				break;
		}
		a[end + 1] = tmp;
	}
}
QuickSort(int* a,int left,int right)
{
    if(left < right)
        return;
    int keyi = QuickSort_along(a,left,right);
    if(keyi - left > 8)
        InserSort(a,keyi);
    else
    {
        QuickSort(a,left,keyi - 1);
        QuickSort(a,keyi + 1,right);
    }
}

除了上面的方法还有两种 单排的方法:

1.挖坑法:

int QuickSort_along(int* a, int left, int right)
{
	// 三数取中优化
	int mid = GetMidIndex(a, left, right);
	swap(a[mid], a[left]);
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		// 先从右边找小 赋值挖坑
		while (a[right] >= key && (left < right))
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;

		// 先从左边找大 赋值挖坑
		while (a[left] <= key && (left < right))
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

2.前后指针法:

int QuickSort_along(int* a, int left, int right)
{
	int mid = GetMidIndex(a, left, right);
	swap(a[mid], a[left]);
    int keyi = left;
    int prev = left;
    int cur = prev + 1;
    while(cur >= right)
    {
        if(a[cur] < a[keyi] && ++prev != cur)
            swap(&a[cur],&a[prev]);
        cur++;
    }
    return prev;
}

最后还有一个:非递归的快速排序:

利用数据结构的栈非递归快排
其实就是将递归的思想用代码表现出来,利用了栈的后进先出的特性。

void QuickSort_Stack(int* a, int left, int right)
{
	Stack SL;
	StackInit(&SL);
	StackPush(&SL,left);
	StackPush(&SL,right);
	while (!StackEmpty(SL))
	{
		int end = StackFront(SL);
		StackPop(&SL);
		int begin = StackFront(SL);
		StackPop(&SL);
		/*if (begin >= end)
		{
			continue;
		}*/
		int keyi = PartSort2(a, begin, end);
		if (keyi + 1 < end)
		{
			StackPush(&SL, keyi + 1);
			StackPush(&SL, end);
		}
		if (begin < keyi - 1)
		{
			StackPush(&SL, begin);
			StackPush(&SL, keyi - 1);
		}
	}
	StackDestory(&SL);
}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到