快速排序:快速排序如果基准值选的好的,话可以很快的排好序,如果没选好可能会超时,所有我们需要优化快速排序,优化操作有我知道的目前与下面两种:
1.可以只有一边递归,另一边循环遍历,减少递归操作,还有基基准值可以选择三点取中法,适用于想链表,数据最大值和最值差距大的数组;
2.当左右指针小于这个设置一个固定值为了避免快速排序效率低(一般有序的数组会效率很低),改用无监督partition法,就是不设置边界条件,将最小值放到第一位,适用于一组有序的数字,数字差距不大
排序代码如下
let threshold = 16;
let quick_sort = function(arr,l,r){
//当左右差距大于16时就用快速排序
while(r - l > threshold){
//定义两个指针x,y ,m是利用三点取中法选出来的
let x = l, y = r, m = middle(arr[l],parseInt((arr[l] + arr[y]) / 2),arr[r]);
do{
//左边小于基准值的不用动
while(arr[x] < m) x ++;
//右边大于基准值的不需要动
while(arr[y] > m) y --;
//当x小于等于y的时候说明没有交叉,此时xy分别不满足小于和大于基准值的条件,就将两个交换就可以达到左边都小于基准值,右边都大于基准值的要求
if(x <= y){
let mm = arr[x]
arr[x] = arr[y];
arr[y] = mm;
x ++,y --;
}
}while(x <= y);
//右边继续递归,此时只要从x开始就好了,此时x -1 = y + 1 就是基准值的位置
quick_sort(arr,x,r);
//左边继续循坏排序,此时的右边就是基准值位置
r = y;
}
return arr;
}
//无监督partition法,就是不设置边界条件,将最小值放到第一位
let find_sort = function(arr,l,r){
//顶一个变量来保存最小值的索引
let min = l;
//循环遍历查找最小值,因为默认就是l所以从l下一位开始找
for(let i = l + 1; i <= r; i ++){
min = arr[i] < arr[min] ? i : min;
}
//从索引最小值开始将最小值放到第一位
while(min > l){
if(arr[min] < arr[min -1]){
let mm = arr[min];
arr[min] = arr[min -1];
arr[min - 1] = mm;
}
min --;
}
//一次遍历每个值,将最小值一次替换到前面,实现排序,因为是用当前值跟前一个值作比较所以从2开始
for(let i = l + 2; i <= r; i ++){
let j = i;
while(arr[j] < arr[j -1]){
let mm = arr[j];
arr[j] = arr[j -1];
arr[j -1] = mm;
j --;
}
}
return arr;
}
//三点取中间法
let middle = function(l,m,r){
let mm = l;
if(l > m) {
l = m;
m = mm;
}
mm = l;
if(l > r){
l = r;
r = mm;
}
mm = m;
if(m > r){
m = r;
r = mm;
}
return m;
}
912. 排序数组
给你一个整数数组 nums,请你将该数组升序排列。
解题思路:
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
思路:先用排序的方法,选择一个基准值,小于基准值的都放在左边,大于基准值的都放在右边,如果左右指针差小于16的话就用插入排序法,代码如下:
var sortArray = function(nums) {
便捷条件
if(nums.length < 2) return nums;
//优先试用快速排序
nums = quick_sort(nums,0,nums.length - 1);
//快速拍讯后在使用插入排序
nums = find_sort(nums,0,nums.length - 1);
return nums;
};
let threshold = 16;
let quick_sort = function(arr,l,r){
//当左右差距大于16时就用快速排序
while(r - l > threshold){
//定义两个指针x,y ,m是利用三点取中法选出来的
let x = l, y = r, m = middle(arr[l],parseInt((arr[l] + arr[y]) / 2),arr[r]);
do{
//左边小于基准值的不用动
while(arr[x] < m) x ++;
//右边大于基准值的不需要动
while(arr[y] > m) y --;
//当x小于等于y的时候说明没有交叉,此时xy分别不满足小于和大于基准值的条件,就将两个交换就可以达到左边都小于基准值,右边都大于基准值的要求
if(x <= y){
let mm = arr[x]
arr[x] = arr[y];
arr[y] = mm;
x ++,y --;
}
}while(x <= y);
//右边继续递归,此时只要从x开始就好了,此时x -1 = y + 1 就是基准值的位置
quick_sort(arr,x,r);
//左边继续循坏排序,此时的右边就是基准值位置
r = y;
}
return arr;
}
//无监督partition法,就是不设置边界条件,将最小值放到第一位
let find_sort = function(arr,l,r){
//顶一个变量来保存最小值的索引
let min = l;
//循环遍历查找最小值,因为默认就是l所以从l下一位开始找
for(let i = l + 1; i <= r; i ++){
min = arr[i] < arr[min] ? i : min;
}
//从索引最小值开始将最小值放到第一位
while(min > l){
if(arr[min] < arr[min -1]){
let mm = arr[min];
arr[min] = arr[min -1];
arr[min - 1] = mm;
}
min --;
}
//一次遍历每个值,将最小值一次替换到前面,实现排序,因为是用当前值跟前一个值作比较所以从2开始
for(let i = l + 2; i <= r; i ++){
let j = i;
while(arr[j] < arr[j -1]){
let mm = arr[j];
arr[j] = arr[j -1];
arr[j -1] = mm;
j --;
}
}
return arr;
}
//三点取中间法
let middle = function(l,m,r){
let mm = l;
if(l > m) {
l = m;
m = mm;
}
mm = l;
if(l > r){
l = r;
r = mm;
}
mm = m;
if(m > r){
m = r;
r = mm;
}
return m;
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
示例:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
解题思路:这题比较单间,就是设置两个指针一个从左边开始遍历,一个从右边遍历,左边存放奇数,右边存放偶数,两边如果不满足要求了,就将两边互换,代码如下:
var exchange = function(nums) {
if(nums.length < 2) return nums;
//设置两个指针,有个最左边,一个组右边
let x= 0, y = nums.length - 1,length = nums.length;
do{
//排查左边已有的奇数将不是奇数的数找出来
while(nums[x] % 2 != 0 && x <= length) x ++;
//排查又边已有的偶数将不是偶数的数找出来
while(nums[y] % 2 == 0 && y > 0) y --;
如果此时 x 小于y那就说明这两个值不符合要求需要换位置
if(x <= y ){
let mm = nums[x];
nums[x] = nums[y];
nums[y] = mm;
x ++;
y --;
}
}while(x <= y);
return nums;
};
148. 排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
例 1:
输入: head = [4,2,1,3]
输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]
示例 3:
输入: head = []
输出: []
解题思路:连接最有的排序方式还没学到,目前就学到了快速排序,所以链表的特性原因,直接用快速排序就可以了,代码如下:
var sortList = function(head) {
if(!head || !head.next) return head;
//定义两个空链表用来储存小于和大于基准值的值,计算基准值
let h1 = null, h2 = null;
//定义两个变量,l保存小值,r保存最大值,都默认是第一个数
let l = head.val, r = head.val;
//定义 两个链表,方便操作,不改变原本链表内容
let p = q = head;
//遍历找到最大值和最小值
while(p){
l = p.val > l ? l : p.val;
r = p.val > r ? p.val : r;
p = p.next;
}
//如果l = r就没必要再继续下去了,会死循环
if( l == r ) return head;
//计算基准值
let m = parseFloat((l + r)/2);
//重置p
p = head;
while(p){
//利用q保存剩下的链表方便循环遍历下去
q = p.next;
//小于基准值保存到h1中
if(p.val <= m){
p.next = h1;//断裂链表,
h1 = p; //将当前值储存到h1达到保存链表功能
}else{
//同上
p.next = h2;
h2 = p;
}
p = q;
};
//递归左边和右边链表达到从小达到的排序目的
h1 = sortList(h1);
h2 = sortList(h2);
p = h1;
//找到h1尾结点
while(p.next) p = p.next;
//h1尾结点指向h2达到两个链表合并
p.next = h2;
return h1;
};
75. 颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
解题思路:这题的数组是由若干个0,1,2组成鄂数组,所有这题适合用无监督partition法,代码如下:
var sortColors = function(nums) {
return find_soret(nums);
};
let find_soret = function(arr){
let min = 0;
//找到最小值的索引
for(let i = 1; i < arr.length; i ++ ){
min = arr[i] < arr[min] ? i : min;
}
//将最小值放到数组第一位,达到不设边界条件的要求
while(min > 0){
let mm = arr[min];
if(arr[min] < arr[min - 1]){
arr[min] = arr[min - 1];
arr[min - 1] = mm;
}
min --;
}
//一次循环遍历将当前值与前一位比较,小就已移到前面,达到从小到大的排序
for(let i = 2; i < arr.length; i ++){
let j = i;
while(arr[j] < arr[j - 1]){
let mm = arr[j];
arr[j] = arr[j -1];
arr[j - 1] = mm;
j --;
}
}
return arr;
}