算法常识——快速排序
前言
什么是快速排序?
首先问道:比如说从大到小,如何确定一个数组中的一个数已经排好了顺序?
这个当然有很多种方式,比如说排序的时候最大值在最左边,那么确定了最左边的位置,这就是冒泡的最基本的原理。
快速排序同样有自己的规则,如果一个数的右边都小于他,左边都大于他,是否这个数不需要变换位置?
答案是,是的。
快速排序就是这个原理,快速排序又分为了:
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;
}