算法常识——快速排序

前言

什么是快速排序?
首先问道:比如说从大到小,如何确定一个数组中的一个数已经排好了顺序?
这个当然有很多种方式,比如说排序的时候最大值在最左边,那么确定了最左边的位置,这就是冒泡的最基本的原理。
快速排序同样有自己的规则,如果一个数的右边都小于他,左边都大于他,是否这个数不需要变换位置?
答案是,是的。
快速排序就是这个原理,快速排序又分为了:
1.双边循环排序;
2.单边循环排序;
这个下面具体会介绍到。
一些术语:
基准元素:当前需要确定的元素,也就是对比元素。

双边循环排序

参考:程序员小灰这本书

比如说我要对4进行排序,那么,我需要做的事比较1,因为1<4,那么继续往后2小与4,然后继续往后,4<6,保留当前6的索引。
然后从右开始,是7,7>4,那么继续往前,然后8也大于,继续,然后是2小于,那么6和2交互位置。然后一直循环下去,当left等于right,那么结束,并且4和left等于right这个位置上交换数字。
下面使用的递归方式,代码也相当简单。

static void Main(string[] args)
{
	int[] intarr = new int[] {1,6,8,2,3,5,10,48,9 };
	quickSort(intarr,0,intarr.Length);
}

public static void quickSort(int []arr,int startIndex,int endIndex)
{
	if (startIndex == endIndex)
	{
		return;
	}
	var baseIndex=changeIndex(arr,startIndex,endIndex);
	quickSort(arr,startIndex,baseIndex-1);
	quickSort(arr, baseIndex+1, endIndex);
}

public static int changeIndex(int []arr,int startIndex,int endIndex)
{
	var baseNum = arr[startIndex];
	// 为什么要从新赋值?startIndex 和 endIndex 是范围的意思,left 和 right 是对比变化的意思。
	var right = endIndex;
	var left = startIndex;
	while (right != left)
	{
		while (right<left&&arr[left]<= baseNum)
		{
			left++;
		}
		while (right<left&& arr[right] > baseNum)
		{
			right--;
		}
		if (right != left)
		{
			var temp = arr[left];
			arr[left] = arr[right];
			arr[right] = temp;
		}
	}
	arr[startIndex] = arr[left];
	arr[left] = baseNum;
	return left;
}

单边循环排序

单边循环 如下图:

比如说4大于1那么不变,然后4>2同样不变,然后到5,5>4,将这个标记记为mark,就是说这个以后要用一个小的数字来替换,然后4>3,所以用3和5替换掉。
我一开始是有一个疑问的,如果后面继续是比5大的数呢?mark是否要移动过去,其实是不用的,因为这样的用途是把小于4的尽量往左迁移,这才是真正的用途。
代码比较简单如下:

static void Main(string[] args)
{
	int[] intarr = new int[] {1,6,8,2,3,5,10,48,9 };
	quickSortSingle(intarr,0,intarr.Length);
}
public static void quickSortSingle(int[] arr, int startIndex, int endIndex)
{
	if (startIndex >= endIndex)
	{
		return;
	}
	var baseIndex = changeSingleIndex(arr, startIndex, endIndex);
	quickSortSingle(arr, startIndex, baseIndex - 1);
	quickSortSingle(arr, baseIndex + 1, endIndex);
}

public static int changeSingleIndex(int[] arr, int startIndex, int endIndex)
{
	var baseNum = arr[startIndex];
	// 为什么要从新赋值?startIndex 和 endIndex 是范围的意思,left 和 right 是对比变化的意思。
	var left = startIndex;
	var mark = startIndex;
	var temp = 0;
	for (var i=left+1;i<=endIndex;i++)
	{
		if (baseNum > arr[i])
		{
			mark++;
			temp = arr[mark];
			arr[mark] = arr[i];
			arr[i] = temp;
			left = mark;
		}
	}
	arr[startIndex] = arr[left];
	arr[left] = baseNum;
	return left;
}

不用递归实现

static void Main(string[] args)
{
	int[] intarr = new int[] {1,6,8,2,3,5,10,48,9 };
	quickSortSingle(intarr,0,intarr.Length);
}
public static void quickSortSingle(int[] arr, int startIndex, int endIndex)
{
	Stack stack = new Stack();
	Tuple<int, int> tuple = new Tuple<int, int>(startIndex, endIndex);
	stack.Push(tuple);
		//
	while (stack.Count!=0)
	{
		var oc= (Tuple<int, int>)stack.Pop();
		if (oc.Item1< oc.Item2)
		{
			var baseIndex= changeSingleIndex(arr, oc.Item1, oc.Item2);
			Tuple<int, int> temp = new Tuple<int, int>(startIndex, baseIndex - 1);
			Tuple<int, int> temp2 = new Tuple<int, int>(baseIndex +1 , endIndex);
			stack.Push(temp);
			stack.Push(temp2);
		}
	}
}

public static int changeSingleIndex(int[] arr, int startIndex, int endIndex)
{
	var baseNum = arr[startIndex];
	// 为什么要从新赋值?startIndex 和 endIndex 是范围的意思,left 和 right 是对比变化的意思。
	var left = startIndex;
	var mark = startIndex;
	var temp = 0;
	for (var i=left+1;i<=endIndex;i++)
	{
		if (baseNum > arr[i])
		{
			mark++;
			temp = arr[mark];
			arr[mark] = arr[i];
			arr[i] = temp;
			left = mark;
		}
	}
	arr[startIndex] = arr[left];
	arr[left] = baseNum;
	return left;
}
posted @ 2020-02-03 13:00  敖毛毛  阅读(219)  评论(0编辑  收藏  举报