排序算法
1. 快速排序
第一步:找到一个基准数,大于该数的放在数组的右边,小于该数的放在左边
第二步:分别对左右进行同样的操作,依次遍历,直到排好序
时间复杂度: O(N*logN) 空间复杂度o(logN) 不稳定算法
优化思路:
基准数的选取,可以采用随机数,也可采用三数取中法
当分隔开的子序列小于一定大小时,采用插入排序代替快速排序(因为递归操作,在小数组的时候快排比插入排序慢)
static void Main(string[] args)
{
int[] arr = { 3, 2, 6, 5, 4, 7, 8, 9, 4, 5, 6, 7, 3, 2, 1, 9 };
quickSort(arr, 0, arr.Length - 1);
foreach(int item in arr)
{
Console.WriteLine(item);
}
Console.WriteLine("Hello World!");
}
//快速排序时间复杂度 0(N2)
// 快排的空间复杂度是o( N)
//若快速排序选取的中间值是随机的一个,时间复杂度是o(N * logN)
public static void quickSort(int[] arr, int left, int right)
{
if(left < right)
{
//
if(left > right - 60)
{
//在arr[left,right]上使用插入排序
//o(N2)在小样本量上跑得快,因为o(N2)在小样本量瓶颈低,且插入的常数时间是极低的,
//
return;
}
Random rd = new Random();
//获取一个随机的比对数,可减少时间复杂度,将其交换到最后一个
swap(arr, left + (int)(rd.Next(right - left + 1)), right);
int[] p = partition(arr, left, right);
quickSort(arr, left, p[0]);
quickSort(arr, p[1] + 1, right);
}
}
public static int[] partition(int[] arr, int left, int right)
{
int index1 = left;
int index2 = right;
int temp = arr[right];
for(int i = index1; i < index2; i++)
{
if(arr[i] < temp)
{
swap(arr, index1, i);
index1++;
}else if(arr[i] > temp)
{
swap(arr, index2, i);
index2--;
i--;
}
}
arr[index2] = temp;
return new int[] { index1, index2 };
}
public static void swap(int[] arr, int left, int right)
{
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
2.归并排序
第一步:采用经典的分治策略,首先进行分,将数组分成小数组,递归操作
第二步:将小数组排序,进行合并,依次向上进行合并,直到整个数组变成有序。
时间复杂度: O(N*logN) 空间复杂度o(N) 稳定算法
public static void process(int[] arr, int left, int right)
{
if(left == right)
{
return;
}
int mid = left + ((right - left) >> 1);
process(arr, left, mid);
process(arr, mid + 1, right);
MergeSort(arr, left, mid, right);
}
public static void MergeSort(int[] arr, int left, int mid, int right)
{
int i = 0;
int p1 = left;
int p2 = mid + 1;
int[] help = new int[right - left + 1];
while (p1 <= mid && p2 <= right)
{
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while(p1 <= mid)
{
help[i++] = arr[p1++];
}
while(p2 <= right)
{
help[i++] = arr[p2++];
}
for(i = 0; i < help.Length; i++)
{
arr[left + i] = help[i];
}
}
3.堆排序
第一步:构建大根堆
第二步:将大根堆的第一个数和最后一个数进行互换,
第三步:将0 - 0(n-1) 的数重新构建成大根堆,同样是第一个数和大根堆的最后一个数进行互换,直到排好序
时间复杂度:O(N*logN) 空间复杂度: o(1) 不稳定算法
public static void headSort(int[] arr)
{
if(arr == null || arr.Length < 2)
{
return;
}
int heapSize = arr.Length;
//构建大根堆
//for(int i= 0; i < arr.Length; i++) // 时间复杂度 o(N)
//{
// headInsert(arr, i); //时间复杂度o(logN)
//}
//替换构建大根堆的方法,可以实现构建部分的时间复杂度为(N + 1/2N + 1/3N + 1/4N .....) = o(N)
for(int j = arr.Length - 1; j >= 0; j--)
{
heapify(arr, j, arr.Length);
}
swap(arr, 0, --heapSize);
while (heapSize > 0) // 时间复杂度 o(N)
{
heapify(arr, 0, heapSize); //时间复杂度o(logN)
swap(arr, 0, --heapSize);
}
}
public static void heapify(int[] arr, int index , int heapSize)
{
int left = 2 * index + 1 ;
while( left < heapSize)
{
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if(largest == index)
{
break;
}
swap(arr, largest, index);
index = largest;
left = 2 * index + 1;
}
}
public static void headInsert(int[] arr, int index)
{
while (arr[index] > arr[(index - 1) / 2])
{
swap(arr, index, ((index - 1) / 2));
index = (index - 1) / 2;
}
}
public static void swap(int[] arr , int a , int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}