牛客网视频总结2
本期目录
双指针问题
任务一:左右排序
给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
要求额外空间复杂度为
O
(
1
)
O(1)
O(1),时间复杂度为
O
(
N
)
O(N)
O(N)
答:同下,但是只存在小于等于区域就好了,如果小于等于,则交换小于等于区域的下一个数字
任务二:荷兰国旗问题
给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。
要求额外空间复杂度为
O
(
1
)
O(1)
O(1),时间复杂度为
O
(
N
)
O(N)
O(N)
答:
三个类似指针的东西,一个代表less,一个代表more,一个代表cur
假设num=5
若当前位置的数=5,cur指针后移;
若当前位置的数<5,小于区域扩大,less++,cur++;
若当前位置的数>5,大于区域扩大,more–,cur不变(因为不确定交换过来的是什么数)
终止条件:cur==more
public static int[] partition(int[] arr, int L, int R, int num){
less = L-1;
more = R+1;
cur = L;
while(cur < more){
if(arr[cur]=num){
cur++;
}
else if(arr[cur]<num){
swap(arr, ++less, cur++);
}
else{
swap(arr, --more, cur);
}
}
return new int[]{less+1,more-1};
}
public static void swap(int[] arr, int a, int b){
int tem = arr[a];
arr[a] = arr[b];
arr[b] = tem;
}
快速排序
普通快排(类似荷兰国旗问题)
取固定位置,一般是取第一个或最后一个称为基准,然后就是比基准小的放在左边,比基准大的放到右边。就是和基准进行交换,这样交换完左边都是比基准小的,右边都是比较基准大的,这样就将一个数组分成了两个子数组,然后再按照同样的方法把子数组再分成更小的子数组,直到不能分解为止。
问题:最差情况是挑出来的最后一个数就是最大或者最小的,因为这样每次等同于只能排好最后一个,时间复杂度为 O ( N 2 ) O(N^2) O(N2);最好情况是挑出来的最后一个数刚好在中间,能二分继续排,这样时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)。由此可得,经典快排的复杂度是和数据有关的。
随机快排(改进后的快排)
随机快排绕开了原本的数据状况,用概率学的角度解决了原本数据存在的问题。也就是说在随即快排中,不寻找最后一个数作为基准,而是等概率的从数组中任意挑选一个数和当前最后一个数进行交换,使得挑中的数作为基准。因此用概率累加计算复杂度,长期期望的复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)
随机快排的额外空间复杂度为 O ( l o g N ) O(logN) O(logN),因为需要记录存下来的断点,断点数量就是递归的深度。随机快排的额外空间复杂度比归并排序的额外空间复杂度低!
堆的简单了解
完全二叉树
- 满二叉树(所有非叶节点全有2个孩子);
- 非满二叉树,但从左向右补全节点
父子节点索引
1个节点i的左孩子节点索引为
2
∗
i
+
1
2*i+1
2∗i+1;右孩子节点索引为
2
∗
i
+
2
2*i+2
2∗i+2;父节点为
(
i
−
1
)
/
2
(i-1)/2
(i−1)/2。
大根堆、小根堆
堆就是完全二叉树。在这棵完全二叉树中,任何一棵子树的最大值都是头部的完全二叉树就是大根堆;相反最小值都是头部的完全二叉树就是小根堆。
整个数组变大根堆(heapInsert)
2,1,3,6,0,4
- 先看[2,1],排好大根堆为[2,1]
- 再把3加进去,3跟父节点2比较,交换[3,1,2],然后3跟子节点比较不交换,得到[3,1,2]
- 再把6加进去,跟父节点2比较,交换为[3,6,2,1],然后再跟父节点3比较,交换[6,3,2,1],然后6跟子节点比较,不交换,得到[6,3,2,1]
- 再把0加进去,跟父节点3比较,不交换,得到[6,3,2,1,0]
- 再把4加进去,跟父节点2比较,交换[6,3,4,1,0,2],4再和父节点6比较不交换,得到[6,3,4,1,0,2]
每一步都可能向上看 O ( l o g N ) O(logN) O(logN),总时间复杂度为 l o g 1 + l o g 2 + . . . + l o g N − 1 log1+log2+...+logN-1 log1+log2+...+logN−1,经计算得总体时间复杂度为 O ( N ) O(N) O(N)
public static void heapInsert(int[] arr, int index){
while(arr[index]>arr[(index-1)/2]){
swap(arr, index, (index-1)/2);
index = (index-1)/2;
}
}
大根堆某数据变小(heapify)
6,5,4,2,5,2,如果把6变成0,则大根堆如何变化
答:向下找左右孩子,跟大的那个做交换
- [0,5,4,2,5,2],0和左子节点5右子节点4比较,和5交换得[5,0,4,2,5,2]
- 0和左子节点2右子节点5比较,和5交换得[5,5,4,2,0,2]
public static void heapify(int[] arr, int index, int heapSize){
int left = 2*index+1;
int right = 2*index+2;
while(left<heapSize){
int max = right < heapSize && arr[left] < arr[right] ? right : left;
int largest = arr[index] > arr[max] ? index : max;//注意方向问题,如果没有右子节点,得用左子节点
if(largest==index){
break;
}
swap(arr, index, largest);
index = largest;
left = index*2+1;
right = index*2+2;
}
}
从数据流中快速找到中位数
- 创一个大根堆和一个小根堆(期望每个堆里的数量都是 N / 2 N/2 N/2),第一个数放大根堆
- 如果大根堆数量超了,则把大根堆根部丢掉,放给小根堆,然后heapify;如果小根堆数量超了,则把小根堆根部丢掉,放给大根堆,然后heapify。
堆排序
- 数组变成大根堆(大根堆未必有序)
- 根部数据和最后一个数据进行交换,这样就能找到一个最大值
- 除最后位置外,其他部分做heapify
- 重复2.3步,直到堆的大小为0
时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN)
public class Heapsort {
public static void main(String[] args) {
int[] arr = new int[]{4,2,4,6,7,8,3,0};
heapSort(arr);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
public static void heapInsert(int[] arr, int index){
while(arr[index]>arr[(index-1)/2]){
swap(arr, index, (index-1)/2);
index = (index-1)/2;
}
}
public static void heapify(int[] arr, int index, int heapSize){
int left = 2*index+1;
int right = 2*index+2;
while(left<heapSize){
int max = right < heapSize && arr[left] < arr[right] ? right : left;
int largest = arr[index] > arr[max] ? index : max;
if(largest==index){
break;
}
swap(arr, index, largest);
index = largest;
left = index*2+1;
right = index*2+2;
}
}
public static void heapSort(int[] arr){
if(arr==null || arr.length==1){
return;
}
for(int i=1;i<arr.length;i++){
heapInsert(arr, i);
}
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while(heapSize>0){
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
public static void swap(int[] arr, int a, int b){
int tem = arr[a];
arr[a] = arr[b];
arr[b] = tem;
}
}