class2 快排、堆排和比较器(左程云左神算法 初级笔记 2018)

class 2

  • 荷兰国旗问题
    给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)
public class NetherlandsFlag {
    public static void netherlandsFlag(int[] arr,int L,int R,int num){
        int min = L-1;
        int max = R+1;
        while(L < max){
            if(arr[L]<num){
                swap(arr,L++,++min);
            }else if(arr[L]>num){
                swap(arr,L,--max);
            }else{
                L++;
            }
        }
    }
    
//    public static int[] netherlandsFlag(int[] arr, int l, int r, int p) {
//        int less = l - 1;
//        int more = r + 1;
//        while (l < more) {
//            if (arr[l] < p) {
//                swap(arr, ++less, l++);
//            } else if (arr[l] > p) {
//                swap(arr, --more, l);
//            } else {
//                l++;
//            }
//        }
//        return new int[] { less + 1, more - 1 }; //返回等于num的部分的位置
//    }

    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {2,4,5,1,9,7,6,7,0};
        int num = 5;
        netherlandsFlag(arr,0,arr.length-1,num);
        System.out.println(Arrays.toString(arr));
    }
}
  • 经典快速排序(划分区域很可能会偏移,和数据状况有关)
package com.godzuo.java;

import java.util.Arrays;

/**
 * @author quanquan
 * @create 2020-04-19-22:39
 */
public class QuickSort {
    public static void quickSort(int[] arr){
        if(arr == null || arr.length < 2) return;
        quickSort(arr,0,arr.length-1);
    }
    public static void quickSort(int[] arr,int L,int R){
        if(L < R) {
            int[] p = partition(arr, L, R);
            quickSort(arr, L, p[0] - 1);
            quickSort(arr, p[1] + 1, R);
        }
    }
    //数组的第最后一个数作为标准进行划分,再把最后一个数放到正确的位置
    //空间复杂度O(1),时间复杂度O(N),做不到稳定性
    public static int[] partition(int[] arr,int L,int R){
        int min = L-1;
        int max = R;
        int num = arr[R];
        while(L < max){
            if(arr[L]<num){
                swap(arr,L++,++min);
            }else if(arr[L]>num){
                swap(arr,L,--max);
            }else{
                L++;
            }
        }
        swap(arr, max, R);
        return new int[] {min+1,max};
    }
    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static void main(String[] args) {
        int[] arr = {2, 4, 5, 1, 9, 7, 6, 7, 0,3,7};
        quickSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

DAY3

  • 随机快速排序 – 用荷兰国旗问题来改进快速排序,随机选择一个数作与最后一个数交换,进行经典快速排序

时间复杂度O(N*logN),额外空间复杂度O(logN)(记下断点位置)

   //加人一行代码,交换位置
   public static void quickSort(int[] arr,int L,int R){
        if(L < R) {
            swap(arr,R,L+(int)Math.random()*(R-L+1));
            int[] p = partition(arr, L, R);
            quickSort(arr, L, p[0] - 1);
            quickSort(arr, p[1] + 1, R);
        }
    }
  • 堆 – 完全二叉树
    • 大根堆 – 完全二叉树中任何一个子树的头部都是最大值
    • 小根堆 – 完全二叉树中任何一个子树的头部都是最小值
  • 堆排序
package com.godzuo.java;

import java.util.Arrays;

/**
 * @author quanquan
 * @create 2020-04-20-15:36
 */
public class HeapSort {
    public static void heapSort(int[] arr){
        if(arr == null || arr.length < 2) return;
        for(int i=0;i<arr.length;i++){
            heapInsert(arr,i);
        }
//        int size = arr.length;
//        swap(arr, 0, --size);
//        while (size > 0) {
//            heapify(arr, 0, size);
//            swap(arr, 0, --size);
//        }
        int heapSize = arr.length-1;
        while (heapSize>0){
            swap(arr,0, heapSize);
            heapify(arr,0,heapSize--);
        }
    }

    //建立大根堆
    public static void heapInsert(int[] arr,int i){
       while (arr[i]>arr[(i-1)/2]){
            swap(arr,i,(i-1)/2);
            i = (i-1)/2;
       }
    }
    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    //修改大根堆
    public static void heapify(int[] arr,int index,int heapSize){
        int left = index*2+1;
        while (left < heapSize){
            int largest = left+1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index){
                break;
            }
            swap(arr,largest,index);
            index = largest;
            left = index*2+1;
        }
    }

    public static void main(String[] args) {
        int[] arr = {5,6,2,8,1,4,9,0,7};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
  • 排序算法的稳定性及其汇总

    • 任何一个相同的值在原始序列中相对次序是不变的

      1.冒泡排序可以实现成稳定的(相同的值不交换)

      2.插入排序可以实现成稳定的

      3.选择排序不稳定

      4.归并排序可以实现成稳定的(保证左边和右边相等时先拷贝左边)

      5.快速排序不稳定

      6.堆排序不稳定

  • 工程中的综合排序算法

    • 样本量很小(小于60) – 直接用插排(因为常数项低)
    • 数组中是基础类型数据(如:int,long,short等) – 快排(因为基础类型不用区分原始顺序)
    • 数组中是自己定义的数据类型 – 归并
  • 补充

    • 归并排序的额外空间复杂度可以变成O(1),但是非常难,不需要掌握,可以搜“归并排序 内部缓存法”
    • 快速排序可以做到稳定性问题,但是非常难,不需要掌握,可以搜“01 stable sort”
    • 有一道题目,是奇数放在数组左边,偶数放在数组右边,还要求原始的相对次序不变,碰到这个问题,可以怼面试官。这就相对于快排过程中的partition过程,无法稳定
  • 比较器

package com.godzuo.java;

import java.util.Arrays;
import java.util.Comparator;

public class MyComparator {

	public static class Student {
		public String name;
		public int id;
		public int age;

		public Student(String name, int id, int age) {
			this.name = name;
			this.id = id;
			this.age = age;
		}
	}

	public static class IdAscendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o1.id - o2.id;
		}

	}

	public static class IdDescendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o2.id - o1.id;
		}

	}

	public static class AgeAscendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o1.age - o2.age;
		}

	}

	public static class AgeDescendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o2.age - o1.age;
		}

	}

	public static void printStudents(Student[] students) {
		for (Student student : students) {
			System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
		}
		System.out.println("===========================");
	}

	public static void main(String[] args) {
		Student student1 = new Student("A", 1, 23);
		Student student2 = new Student("B", 2, 21);
		Student student3 = new Student("C", 3, 22);

		Student[] students = new Student[] { student3, student2, student1 };
		printStudents(students);

		Arrays.sort(students, new IdAscendingComparator());
		printStudents(students);

		Arrays.sort(students, new IdDescendingComparator());
		printStudents(students);

		Arrays.sort(students, new AgeAscendingComparator());
		printStudents(students);

		Arrays.sort(students, new AgeDescendingComparator());
		printStudents(students);
        
        //自带的堆结构
		PriorityQueue<Student> heap = new PriorityQueue<>(new AgeAscendingComparator());
		heap.add(student1);
		heap.add(student2);
		heap.add(student3);
		while (!heap.isEmpty()){
			Student student = heap.poll();
			System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
		}

	}
}
  • 桶排序、计数排序、基数排序
    • 非基于比较的排序,与被排序的样本的实际数据状况很有关系,所以实际中并不经常使用
    • 时间复杂度O(N),额外空间复杂度O(N)
    • 稳定的排序
  • 桶排序
    • 桶相当于安装数据状况设计的容器,然后把元素依次放进属于他的桶中
    • 计数排序是桶排序的一个具体体现
    • 基数排序用的桶比较少
  • 给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序
package com.godzuo.java;

/**
 * @author quanquan
 * @create 2020-04-20-21:41
 */
public class MaxGap {
    public static int maxGap(int[] nums) {
        if (nums == null || nums.length < 2) {
            return 0;
        }
        int len = nums.length;
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < len; i++) {
            min = Math.min(min, nums[i]);
            max = Math.max(max, nums[i]);
        }
        if (min == max) {
            return 0;
        }
        boolean[] hasNum = new boolean[len + 1];
        int[] maxs = new int[len + 1];
        int[] mins = new int[len + 1];
        int bid = 0;
        for (int i = 0; i < len; i++) {
            bid = bucket(nums[i], len, min, max); //找到元素应该放置的桶
            mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i]; //更新桶中最小值
            maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i]; //更新桶中最大值
            hasNum[bid] = true;
        }
        int res = 0;
        int lastMax = maxs[0];
        int i = 1;
        //找到每一个非空桶和距他最近的非空桶,用当前的最小,减前一个最大
        for (; i <= len; i++) {
            if (hasNum[i]) {
                res = Math.max(res, mins[i] - lastMax);
                lastMax = maxs[i];
            }
        }
        return res;
    }

    public static int bucket(long num, long len, long min, long max) {
        return (int) ((num - min) * len / (max - min));
    }
    
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值