链接
题目:
1. 快速排序
基本思想:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
1.1 时间复杂度:
平均时间复杂度是 O(n logn);
最好情况:每一次切分选择的基准数字刚好将当前序列等分成两部分;如果把数组的切分看做是一个树,那么下图就是它的最优情况的图示,共切分了O(logn)次,所以,最优情况下快速排序的时间复杂度为O(n logn);
最坏情况: 每一次切分选择的基准数字是当前序列中最大数或者最小数,这使得每次切分都只比上次少一个元素,那么总共就得切分n次,所以,最坏情况下,快速排序的时间复杂度为O(n^2);
注意:
- 先走右指针,再走左指针 !最后返回的partition也是右指针;
- sort 是递归分界;partition是返回新的基准值,递归拆分时不算上partiton分界值!
sort(nums,lo,partition-1);
sort(nums,partition+1,hi);
而归并排序中:
sort(nums,lo,mid);
// 是mid而不是mid-1
sort(nums,mid+1,hi);
class Solution {
public int[] sortArray(int[] nums) {
int lo=0;
int hi=nums.length-1;
sort(nums,lo,hi);
return nums;
}
// 以分界值为界分割数组
void sort(int[] nums,int lo,int hi){
if(lo>=hi){
return;
}
//前序遍历 得到分界值索引
int partition=partition(nums,lo,hi);
sort(nums,lo,partition-1); // 递归
sort(nums,partition+1,hi);
}
// 返回新的分界值、同时排序
// 指针从两边往中间走!
int partition(int[] nums,int lo,int hi){
// 分界值
int key=nums[lo];
//指针
int left=lo; // 第一个是分界值要跳过
int right=hi+1;
while(true){
// 先走右 !直到小于key则等待交换
while(nums[--right]>key){
if(left>=right){
break; // 越界跳出while
}
}
// 再走左 ! 直到大于Key则等待交换
while(nums[++left]<key){ // 再走左 !
if(left>=right){
break; // 越界跳出while
}
}
// 指针重合则跳出while(true)
if(left>=right){
break;
}
swap(nums,left,right); // ※
}
// 跳出while(true),首元素和指针重合元素(分界节点)
swap(nums,lo,right);
return right; // 返回必须右边
}
void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
2. 归并排序:
基本思想:
先拆分数组为两个子序列,然后将已有序的子序列合并,得到完全有序的序列;
即先使每个子序列有序,再使子序列段间有序;
注意: lo、mid、hi 都是索引 !在 merge中判断时有等号 !!!
2.1 时间复杂度:
每次固定以mid值来切分,所以切分的复杂度是O(logn),所以总共是O(n logn)
注意辅助数组的索引从 lo 开始,而不是从0 开始!
class Solution {
// 辅助数组
int[] s;
public int[] sortArray(int[] nums) {
s=new int[nums.length];
int lo=0;
int hi=nums.length-1;
sort(nums,lo,hi);
return nums;
}
void sort(int[] nums,int lo,int hi){
// base case
if(lo>=hi){
return;
}
int mid=lo+(hi-lo)/2;
sort(nums,lo,mid); // mid而不是mid-1
sort(nums,mid+1,hi);
//后序遍历
merge(nums,lo,mid,hi);
}
//合并两个数组
void merge(int[] nums,int lo,int mid,int hi){
// 三个指针
int i=lo;
int p1=lo;
int p2=mid+1;
while( p1<=mid && p2<=hi ){
if(nums[p1]<=nums[p2]){
s[i++]=nums[p1++];
}else{
s[i++]=nums[p2++];
}
}
// 有一边到头,到头的指针最后会++,超过范围
while(p1<=mid){
s[i++]=nums[p1++];
}
while(p2<=hi){
s[i++]=nums[p2++];
}
//拷贝回原数组 注意j<=hi而不是< !
for(int j=lo;j<=hi;j++){
nums[j]=s[j];
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/89236.html