【苏瞳】C语言+三种快速排序+ 三数取中法优化快排

目录

1.挖坑法

2.左右指针法

3.前后指针法

4.快排优化 -- 三数取中法


快速排序毋庸置疑是最快的排序!?要不对不起它这个名字,哈哈。

这里都以升序为例子。

1.挖坑法

一般都是取第一个做pivot,key=arr[pivot],思想就是使key左边都比key小,右边都比key大。

[left,pivot-1]   pivot   [pivot+1,right]

然后再递归分治arr数组中的左边和右边直到只有一个数字。

单趟排序如下图:

【苏瞳】C语言+三种快速排序+ 三数取中法优化快排_第1张图片

整体代码如下,注释详细:

注意循环里面的循环条件不要忘记 bagin < end ,不然可能会越界。

同样 while里面的找小条件要arr[end] >= key ,这里一定要>=否则可能会死循环。

找大同理。

#include

//快速排序(挖坑法)  O(N*logN) 
//当数组内有序的时候效率最坏 -> O(N^2)  -> 改进(三数取中法)
void QuickSort(int *arr,int left,int right)//闭区间
{
	if(left >= right)//当区间没有值或者只有一个的时候就不用排啦
	{
		return;
	}
	
	int bagin=left,end=right;
	int pivot=bagin;//坑
	int key=arr[bagin]; //关键字
	
	while(bagin < end)
	{
		//1.右边找小的放到左边
		while(bagin < end && arr[end] >= key)//arr[end] >= key 注意这里要>=否则会死循环
		{
			end--;
		}
		//小的放到左边的坑里
		arr[pivot]=arr[end];
		pivot=end;
		//2.左边找大的放到右边
		while(bagin < end && arr[bagin] <= key)
		{
			bagin++;
		}
		//大的放到右边的新坑里
		arr[pivot]=arr[bagin];
		pivot=bagin;
	}
	pivot=bagin;//pivot=end;
	arr[pivot]=key;
	
	//[left,right]
	//[left,pivot-1] pivot [pivot+1,right]
	//如果左 右子区间有序整体就有序了 -> 分治递归
	QuickSort(arr,left,pivot-1);
	QuickSort(arr,pivot+1,right);
}


int main()
{
	int arr[]={5,2,1,4,3,6,9,8,7,0};
    QuickSort(arr,0,10-1);
	for(int i=0;i<10;i++)
	{
		printf("%d ",arr[i]);
	} 
	return 0;
}


2.左右指针法

思想完全和第一种一样,就是这种找比key大或小是用左右两个指针完成的。

整体代码如下,注释详细:

#include

void Swap(int *a,int *b)
{
	int temp=*a;
	*a=*b;
	*b=temp;
}


//挖坑法(变形) —> 左右指针法(思路一样)
void QuickSort(int *arr,int left,int right)//闭区间
{
	if(left >= right)
	{
		return;
	}
	
	int bagin=left,end=right;//左右指针
	int keyi=bagin;
	
	while(bagin < end)
	{
		while(bagin < end && arr[end] >= arr[keyi])//找小的
		{
			end--;
		}
		
		while(bagin < end && arr[bagin] <= arr[keyi])//找大的
		{
			bagin++;
		}
		
		Swap(&arr[bagin],&arr[end]);
	}
	Swap(&arr[bagin],&arr[keyi]);//这时bagin和end相等而且它们都指向中间 —> Swap(&arr[end],&arr[keyi]);
	
	//[left,bagin-1] [bagin] [bagin+1,right]
	QuickSort(arr,left,bagin-1);
	QuickSort(arr,bagin+1,right);
}


int main()
{
	int arr[]={5,2,1,4,3,6,9,8,7,0};
    QuickSort(arr,0,10-1);
	for(int i=0;i<10;i++)
	{
		printf("%d ",arr[i]);
	} 
	return 0;
}

3.前后指针法

这种实现和上面两种略有不同。

还是取第一个值的下标作keyi,利用前面两个指针 prev cur

cur找小,每次遇到比keyi小的值就停下来,prev++,交换prev和cur位置的值

如下图:

【苏瞳】C语言+三种快速排序+ 三数取中法优化快排_第2张图片

整体代码如下,注释详细:

#include

void Swap(int *a,int *b)
{
	int temp=*a;
	*a=*b;
	*b=temp;
}


//挖坑法(变形) —> 前后指针法
void QuickSort(int *arr,int left,int right)//闭区间
{
	if(left >= right)
	{
		return;
	}

    int keyi=left; 
    
    int prev=left;//cur找小,每次遇到比keyi小的值就停下来,prev++,交换prev和cur位置的值
    int cur=left+1; 
    
    while(cur <= right)//闭区间
    {
    	if(arr[cur] < arr[keyi] && (++prev)!=cur)//这样加个判断可以防止prev和cur相等时交换,自己和自己交换没意义
    	{
    		Swap(&arr[prev],&arr[cur]);
		}
//		if(arr[cur] < arr[keyi])//这样写也行
//    	{
//    		prev++;
//    		Swap(&arr[prev],&arr[cur]);
//		}
		cur++;
    }
    Swap(&arr[keyi],&arr[prev]);
    
    //[left,prev-1] [prev] [prev+1,right]
	QuickSort(arr,left,prev-1);
	QuickSort(arr,prev+1,right);
}


int main()
{
	int arr[]={5,2,1,4,3,6,9,8,7,0};
    QuickSort(arr,0,10-1);
	for(int i=0;i<10;i++)
	{
		printf("%d ",arr[i]);
	} 
	return 0;
}


4.快排优化 -- 三数取中法

众所周知,快速排序的时间复杂度为O(N*logN)

什么时候最坏呢?

当然是每次取的key恰好是最大值或者最小值,即数组已经有序,这时的时间复杂度就是O(N^2)

大致如下图:

【苏瞳】C语言+三种快速排序+ 三数取中法优化快排_第3张图片

为了避免这种情况可以,让key取尽量中间值即三数取中法。

整体代码如下,注释详细:

下面是对挖坑法优化,其他方法优化一样,调用一下GetMidIndex函数就行了

#include

void Swap(int *a,int *b)
{
	int temp=*a;
	*a=*b;
	*b=temp;
}

//快速排序 优化 -> (三数取中法)
int GetMidIndex(int *arr,int left,int right)//取中间值下标函数
{
	int mid=(left+right)>>1;//int mid=(left+right)/2;
	if(arr[left] < arr[mid])
	{
		if(arr[mid] < arr[right])// 例如:2(L)  3(mid)   4(R)
		{
			return mid;
		}
		else //arr[mid] > arr[right]  例如:2(L)  4(mid)   3(R)  -> mid已经最大,left right中大的的就是中间值
		{
			if(arr[left] > arr[right])//例如:3(L)  4(mid)   2(R)
			{
				return left;
			}
			else//例如:2(L)  4(mid)   3(R)
			{
				return right;
			}
		}
	}
	else//a[left] > arr[mid]
	{
		if(arr[mid] > arr[right])
		{
			return mid;
		}
		else//arr[mid] < arr[right]  -> mid已经最小,left right中小的的就是中间值
		{
			if(arr[left] < arr[right])
			{
				return left;
			}
			else
			{
				return right;
			}
		}
	}
}


void QuickSort(int *arr,int left,int right)//闭区间
{
	if(left >= right)
	{
		return;
	}
	
	int index=GetMidIndex(arr,left,right);
	Swap(&arr[left],&arr[index]);//交换一下,防止key取到最小值或者最大值
	
	//下面逻辑不变,和挖坑法的一样
	int bagin=left,end=right;
	int pivot=bagin;//坑
	int key=arr[bagin]; //关键字
	
	while(bagin < end)
	{
		//1.右边找小的放到左边
		while(bagin < end && arr[end] >= key)
		{
			end--;
		}
		//小的放到左边的坑里
		arr[pivot]=arr[end];
		pivot=end;
		//2.左边找大的放到右边
		while(bagin < end && arr[bagin] <= key)
		{
			bagin++;
		}
		//大的放到右边的新坑里
		arr[pivot]=arr[bagin];
		pivot=bagin;
	}
	pivot=bagin;//pivot=end;
	arr[pivot]=key;
	
	//[left,right]
	//[left,pivot-1] pivot [pivot+1,right]
	//如果左 右子区间有序整体就有序了 -> 分治递归
	QuickSort(arr,left,pivot-1);
	QuickSort(arr,pivot+1,right);
}


int main()
{
	int arr[]={5,2,1,4,3,6,9,8,7,0};
    QuickSort(arr,0,10-1);
	for(int i=0;i<10;i++)
	{
		printf("%d ",arr[i]);
	} 
	return 0;
}

你可能感兴趣的:(排序算法,笔记,数据结构,排序算法,快速排序,算法,1024程序员节)