牛客网视频总结2(荷兰国旗问题、经典/随机快排、堆排序、堆的简单介绍)

本文详细介绍了双指针在左右排序和荷兰国旗问题中的应用,以及快速排序的原理,包括普通快排和随机快排。此外,还讲解了堆的基本概念,如大根堆、小根堆的构建与维护,并展示了如何从数据流中快速找到中位数。最后,探讨了堆排序的实现过程和时间复杂度。
摘要由CSDN通过智能技术生成

牛客网视频总结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),因为需要记录存下来的断点,断点数量就是递归的深度。随机快排的额外空间复杂度比归并排序的额外空间复杂度低!

堆的简单了解

完全二叉树

  1. 满二叉树(所有非叶节点全有2个孩子);
  2. 非满二叉树,但从左向右补全节点
    在这里插入图片描述

父子节点索引

1个节点i的左孩子节点索引为 2 ∗ i + 1 2*i+1 2i+1;右孩子节点索引为 2 ∗ i + 2 2*i+2 2i+2;父节点为 ( i − 1 ) / 2 (i-1)/2 (i1)/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+...+logN1,经计算得总体时间复杂度为 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;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值