1.时间复杂度和空间复杂度,及稳定性
2.冒泡排序
最好是n,最坏是 n的平方,在内存里排。是稳定的。
void Bubble_Sort(int[] arr) //参数为数组和长度
{
int n=arr.length;
for (int i = 0; i < n ; i++) {
for (int j = 0; j < n - i - 1; j++) { //n-i-1是比较次数,每比较一趟就少一次
if (arr[j] > arr[j + 1]) { //交换arr[j]与arr[j+1]的值
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
3.插入排序
public void insertSort(int [] nums)
{
if(nums.length==0 && nums.length==1)
{
return ;
}
//注意,遍历的最大位置是 长度减2
for(int i=0;i<nums.length-1;i++)
{
//这个border,表示已排数组的右界。
int border=i;
int current =nums[i+1];
//从右往左遍历已排数组,如果比current大,就让当前遍历的这个元素后移一位。
while(border>=0 && current<nums[border])
{
//右移一位
nums[border+1]=nums[border];
border--;
}
//停的时候,说明已经找到 那个小于等于 current的元素的位置了。
nums[border+1]=current;
}
}
4.希尔排序(里面用了直接插入排序)
最佳情况:T(n) = O(n)
最坏情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
不稳定: 需要多次插入排序,值相同的元素可能在各自的插入排序中移动,最后其 稳定 性就会被打乱
算法停止的标识是 gap=0。其实就是每次移动gap个单位的,直接插入排序。
public void HillSort(int [] nums)
{
int gap =(nums.length)/2;
//希尔排序的循环条件是gap>0
while(gap>0)
{
//不同于直接插入排序,这里最大索引到nums.length-1
for(int i=gap;i<nums.length;i++)
{
//i-gap,就是直接插入排序的右界。
int border=i-gap;
//
int current=nums[i];
//这个while,就是一个一次移动gap位置的直接插入排序。
while(border>=0 && nums[border] >current )
{
//往后移动gap个位置
nums[border+gap]=nums[border];
border-=gap;
}
//出来了,说明碰到小于等于current的了。
nums[border+gap]=current;
}
gap=gap/2;
}
}
5.归并排序
最佳情况:T(n) = O(nlog2 n)
最坏情况:T(n) = O(nlog2 n)
平均情况:T(n) =O(nlog2n) 稳定
递归的把一个数组拆分为两个数组,直接数组长度只剩1(也就是分成两个元素)。
就是不断地递归 有序地合并 拆分的两个子数组。
合并的方法就是,有序合并 两个数组,且让新数组有序。
那为什么要递归到最深,也就是两个数组只有一个元素的时候呢?
因为这样复杂度低。如果只整一层归并呢?那样复杂度就是n的平方 即(n/2)(n/2)。
public int[] mergeSort(int[] arr)
{
//当数组大小为1的时候,直接return,让递归的上一层排序即可。
if (arr.length == 1) return arr;
//copyOfRange,左闭右开
int mid= (arr.length)/2;
int [] leftArr=Arrays.copyOfRange(arr,0,mid);
int [] rightArr=Arrays.copyOfRange(arr,mid,arr.length);
//mergeSort 必须要有返回值,因为上一层要用
return merge(mergeSort(leftArr),mergeSort(rightArr));
}
// 就是把两个有序数组, 合并成一个有序数组。
public int[] merge(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
for (int index = 0, i = 0, j = 0; index < result.length; index++) {
//i 是用来遍历left 数组的,i都大于等于left.length了,则说明 left数组的元素都加完了
if (i >= left.length)
result[index] = right[j++];
else if (j >= right.length)
result[index] = left[i++];
else if (left[i] > right[j])
result[index] = right[j++];
else
result[index] = left[i++];
}
return result;
}
6.选择排序
最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
不稳定
就是每一轮都把最小的值放在最前面。
比如第一轮 确定 1为最小的,放在最前面。 下一轮就不用 考虑第一个位置。
第一次排序结果:{1,3,7,2,6,9,5,8}
第二轮就 ,确认2是 第二轮最小的。下一轮就不用 考虑前两个位置。
第二轮排序结果为:{1,2,7,3,6,9,5,8}。
public static int[] selectionSort(int[] array) {
if (array.length == 0)
return array;
for (int i = 0; i < array.length; i++) {
//每个i就是一轮。
int minIndex = i;//最小索引初始化为。
for (int j = i; j < array.length; j++) {
if (array[j] < array[minIndex]) //找到最小的数
minIndex = j; //将最小数的索引保存
}
//然后 位置i 和最小的索引 minIndex 的值交换。
//i 位置就成了这一轮最小的。
int temp = array[minIndex];
array[minIndex] = array[i];
array[i] = temp;
}
return array;
}
7.快速排序
时间复杂度:
- 最佳情况:T(n) = O(nlogn)
- 最差情况:T(n) = O(n的平方)
- 平均情况:T(n) = O(nlogn)
空间复杂度(因为每次都要记录基准值):O(logn)
但是排序不稳定。
给定一个序列 6 1 2 7 9 3 4 5 10 8
在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6。想一想,你有办法可以做到这点吗?
变成如下这样 6 1 2 7 9 3 4 5 10 8。
做法:
- 两个指针 一个从 右往左,一个从左到右。i=left ,j=right。i从基准 开始。
- 基准在左,就先 左移右指针。直到碰到一个小于 基准的元素。然后 i ,j位置元素交换。 然后再右动左指针,让直到碰到一个大于 基准的元素,然后 i ,j位置元素交换。
- 当 i 和 j相等的时候,就是 这一轮 的结束。
- 这种i和 j交叉 行走的方式, i 和 j最后一定会停在同一个位置。
- 最终把temp,插入到位置 i即可。
- 然后递归处理 最终位置 两侧的子数组即可。
void quickSort(int[] arr,int left,int right)
{
if (right >= left) {
//保存基数
int basic = arr[left];
//定义左右指针
int i = left;
int j = right;
while (i < j) { //左指针小于右指针
while (i < j && arr[j] > basic) {//操作右指针找到小于基数的下标
j--;
}
//出了这个while,立马就得判断
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && arr[i] < basic) {//相反,找到大于基数的下标
i++;
}
if (i < j) {
arr[j] = arr[i]; //大于基数的值赋给右指针所指的位置
j--; //右指针左移
}
}
arr[i] = basic; //将基数放入到指针重合处
quickSort(arr, left, i - 1); //递归调用,对左半部分数组进行排序
quickSort(arr, i + 1, right); //对右半部分数组进行排序
}
}
8. 堆排序
堆 是一个完全二叉树。
大根堆:每个节点的值都大于或者等于他的左右孩子节点的值。
跟二叉排序树不同之处是, 孩子节点只需要 小于父亲节点即可。
它满足完全二叉树的一些规则,最后一个非叶子节点是 arrLengrh/2-1