Q1:快速排序
Q2:冒泡排序
Q3:直接插入排序
Q4:折半插入排序
Q5:简单选择排序
归并排序
Q6:数组中最小的K个数
Q7:数据流中的中位数
Unit 7 Q1:快速排序
思想:
1.int Partition(int A[],int low,int high)
每一趟选择当前序列的第一个作为枢轴,将比枢轴小的放左边,比枢轴大的放右边,最后腾出位置放枢轴。
(比枢轴小的数…)枢轴(比枢轴大的数…),返回枢轴所谓位置
2.递归处理左右两边.
空间效率:平均O(logn)。最大递归深度:n。最小递归深度log2(n+1)向上取整
时间效率:最坏O(n^2)。基本有序、基本逆序
最好or平均:O(nlogn)。平均分为两个等长的表,速度更快
public void quicksort(int A[],int low,int high){
if(low<high){
int pivot=Partition(A,low,high);//定好,原来的A[0]现在的位置是pivot
quicksort(A,low,pivot-1);
quicksort(A,pivot+1,high);
}
}
int Partition(int A[],int low,int high){
int temp=A[low];//temp存第一个
//先左后右一直换
while(low<high){
//右碰小换
while(low<high&&A[high]>=temp) high--;
A[low]=A[high];
//左碰大换
while(low<high&&A[low]<=temp) low++;
A[high]=A[low];
}
//都换完了。low位置左边都比temp小,右边都比temp大
A[low]=temp;
return low;//返回定好的第一个的值的位置
}
Unit 7 Q2:快速排序
date:2019/12/3
思想:一趟冒泡将最小的元素交换到最前面,需(n-1)趟冒泡即可排好序
时间复杂度平均O(n ^ 2)。最好情况:初始有序,O(n)。最坏情况:初始逆序O(n^2)
public void bubblesort(int A[]){
int n=A.length;
for(int i=0;i<n-1;i++){
boolean flag=false;//flag表示本趟是否发生交换
for(int j=n-1;j>i;j--){
if(A[j-1]>A[j]){//左边比右边大,换
int temp=A[j-1];
A[j-1]=A[j];
A[j]=temp;
flag=true;
}
}
if(flag==false)//没发生交换,说明有序
break;
}
}
unit 7 Q3:直接插入排序
date:2019/12/3
适用于基本有序。
思想:每趟将未排序序列的第一个插入已排序序列中的相应位置,未排序序列为空时停止。
最好情况:序列有序.O(n)
最坏情况:序列逆序.O(n^2)
public void directInsertSort(int A[]){
int n=A.length;
//从第一个开始插
for(int i=1;i<n;i++){
int temp=A[i];
//从已排序序列最后一个开始找,找到A[j]<temp<A[j+1]停止
int j=i-1;
for(;j>=0&&temp<A[j];j--)
A[j+1]=A[j];
A[j+1]=temp;//上次已经把A[j+1]位置的值移到A[j+2]位置上了
}
}
unit 7 Q4:折半插入排序
date:2019/12/4
思路:
直接插入排序基础上,使用折半查找法确定插入位置。仅适用于顺序表。
与直接插入排序的不同之处:减少了元素间比较的次数(移动次序不变,该移动的还得移动)
public void binaryInsertSort(int A[]){
int i,n=A.length;
for(i=1;i<n;i++){
int temp=A[i];
//折半查找。找到temp的插入位置A[mid+1]
int low=0,high=i-1,mid=(low+high)/2;
while(low<=high){
mid=(low+high)/2;
//终止条件:A[mid] <= temp < A[mid+1]
if(A[mid]<=temp&&temp<A[mid+1])
break;
if(A[mid]<=temp)
low=mid+1;
if(temp<A[mid])
high=mid-1;
}
//找到位置,temp插在A[mid+1]位置上
//把mid+1到i-1,都向后挪一个
for(int j=i-1;j>mid;j--)
A[j+1]=A[j];
//temp插在A[mid+1]位置上
A[mid+1]=temp;
}
}
unit 7 Q5:简单选择排序
date:2019/12/4
思想:
每次从无序中选出一个最小的,将最小的与无序最头上替换,此时最头上有序。
经过(n-1)趟排序后有序。
特点:
时间复杂度O(n^2)。元素很少移动;比较次数与初始序列无关=(1/2)n(n-1)
public void selectsort(int A[]){
int n=A.length;
for(int i=0;i<n-1;i++){
//找无序中最小的,下标记为min_j
int min_j=i;
for(int j=i+1;j<n;j++){
if(A[j]<A[min_j])
min_j=j;
}
//交换
int temp=A[i];
A[i]=A[min_j];
A[min_j]=temp;
}
}
unit 7 Q6:数组中最小的K个数
剑指offer 40 牛客链接
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
date:2020/02/14
思路:
利用快速排序中的获取分割(中轴)点位置函数Partitiion。
目的是找到index==k-1,使得比第k个数字小的所有数字都位于index的左边,比第k个数字大的所有数字都位于index的右边。调整之后,位于数组左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)
时间复杂度O(N)
代码:
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res=new ArrayList<Integer>();
if(input==null||input.length<=0||k<=0||k>input.length)
return res;
int index,start=0,end=input.length-1;
index=Partition(input,start,end);
while(index!=k-1){
//index比实际的位置靠左:小了,向右找
if(index<k-1)
start=index+1;
else
//index靠右了,向左找
end=index-1;
index=Partition(input,start,end);
}
for(int i=0;i<k;i++)
res.add(input[i]);
return res;
}
int Partition(int[] A,int low,int high){
int temp=A[low];
while(low<high){
//右碰小换
while(low<high&&A[high]>=temp) high--;
A[low]=A[high];
//左碰大换
while(low<high&&A[low]<=temp) low++;
A[high]=A[low];
}
A[low]=temp;
return low;
}
unit 7 Q7:数据流中的中位数
剑指offer 41 牛客链接
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
date:2020/02/15
思路:
设置一个大顶堆,一个小顶堆,堆用java中优先队列实现。
//小顶堆、大顶堆的java实现
private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
大顶堆中最大者(根)小于 小顶堆中最小者(根)
- 插入规则
奇数:插入小顶堆;将小顶堆中最小者(根)插入大顶堆
偶数:插入大顶堆;将大顶堆中最大者(根)插入小顶堆 - 取出规则
奇数:小顶堆中取其根节点
偶数:小顶堆、大顶堆根节点的平均值
全部代码:
//小顶堆
private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
//大顶堆。要重写
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//记录偶数个还是奇数个
int count = 0;
//每次插入小顶堆的是当前大顶堆中最大的数
//每次插入大顶堆的是当前小顶堆中最小的数
//这样保证小顶堆中的数永远大于等于大顶堆中的数
//中位数就可以方便地从两者的根结点中获取了
public void Insert(Integer num) {
//奇数
if(count%2==1){
minHeap.offer(num);//插入小顶堆
int min=minHeap.poll();
maxHeap.offer(min);//将小顶堆中最小者(根)插入大顶堆
}else{
//偶数
//插入大顶堆
maxHeap.offer(num);//插入大顶堆
int max=maxHeap.poll();
minHeap.offer(max);//将大顶堆的最大者(根)插入小顶堆
}
count++;
}
public Double GetMedian() {
double res;
//奇数,从小顶堆中取根结点
if(count%2==1)
res=minHeap.peek();
else
//偶数。大顶堆和小顶堆根节点的平均值
res=(minHeap.peek()+maxHeap.peek())/2.0;
return res;
}