跟着左神刷爆算法——简单排序算法(2)

堆结构:完全二叉树结构

i的     左子节点:2*1+1     右子节点:2*1+2     父节点:(i-1)/2

大根堆:完全二叉树,每一颗子树的最大值都是头节点的值就是大根堆

小根堆:完全二叉树,每一颗子树的最小值都是头节点的值就是小根堆

heapInsert过程 实现大根堆

// 某个数现在处在index位置,往上继续移动
	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;
		}
	}

大根堆中 依次删除头节点,再实现大根堆的过程 其实是依次将头节点进行排序:删除头节点,将最后一个节点换到头节点,heapSize-1,指针指向头节点进行heapify

    public static void heapify(int[] arr, int index, int heapSize) {
		int left = index*2+1; // 左孩子的下标
		while(left < heapSize) { // 当下方还有孩子的时候
			// 两个孩子中,谁的值大,把下标给largest 
			// left+1是右孩子,当右孩子还存在 并大于左孩子时,右孩子为largest
			int largest = left + 1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
			// 父和较大孩子之间,谁值大 把下标给largest
			largest = arr[largest] > arr[index]?largest:index;
			if(largest == index) {
				break;
			}
			swap(arr,largest,index);
			index = largest;
			left = index*2+1;
		}
    }

堆排序:时间复杂度:O(N*logN)  空间复杂度O(l)

    public static void heapSort(int[] arr) {
		if(arr == null || arr.length < 2) {
			return;
		}
		// 把数组整体范围搞成大根堆
//		for(int i = 0; i < arr.length; i++) { // O(N)
//			heapInsert(arr, i); // O(logN)
//		}
		// 或者每个位置进行heapify 稍微快一点 复杂度不变
		for(int i = arr.length-1;i >= 0; i--) {
			heapify(arr,i, arr.length);
		}
		// 0位置数和最后一个位置数交换,然后堆大小--
		int heapSize = arr.length;
		swap(arr,0,--heapSize);
		while(heapSize > 0) { // O(N)
			heapify(arr,0,heapSize); // O(logN)
			swap(arr,0, --heapSize); // O(1)
		}
	}}
	}

优先级队列---堆结构

问题:已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离不可以超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

O(N*logN)   

    public void sortArrayDistanceLessK(int[] arr, int k) {
		// 默认小根堆
		PriorityQueue<Integer> heap = new PriorityQueue<>();
		int index = 0;
		for(; index <= Math.min(arr.length, k); index++) {
			heap.add(arr[index]);
		}
		int i = 0;
		for(; index < arr.length; i++, index++) {
			heap.add(arr[index]);
			arr[i] = heap.poll();
		}
		while(!heap.isEmpty()) {
			arr[i++] = heap.poll();
	    }
    }

优先级队列默认小根堆即升序排序,可以 结合比较器进行 降序排序

    public static class AComp implements Comparator<Integer>{

		@Override
		public int compare(Integer arg0, Integer arg1) {
			return arg1 - arg0;
		}
	}
	
	public static void main(String[] args) {
		PriorityQueue<Integer> heap = new PriorityQueue<>(new AComp());
		heap.add(8);
		heap.add(4);
		heap.add(4);
		heap.add(9);
		heap.add(10);
		heap.add(3);
		while(!heap.isEmpty()) {
			System.out.println(heap.poll());
		}
	}

桶排序 :不基于比较的排序

计数排序:

基数排序:  时间复杂度:O(N)

    public static void radixSort(int[] arr) {
		if(arr == null || arr.length < 2) {
			return;
		}
		radixSort(arr, 0, arr.length - 1, maxbits(arr));
	}
	
	// 获取arr中最大值 有多少10进制位
	public static int maxbits(int[] arr) {
		int max = Integer.MIN_VALUE;
		for(int i = 0; i < arr.length; i++) {
			max = Math.max(max, arr[i]);
		}
		int res = 0;
		while(max != 0) {
			res++;
			max /= 0;
		}
		return res;
	}
	// arr[begin...end]排序
	public static void radixSort(int[] arr, int l, int r, int digit) {
		final int radix = 10;
		int i = 0, j = 0;
		// 有多少个数准备多少个辅助空间
		int[] bucket = new int[r - l + 1];
		for(int d = 1; d <= digit; d++) {//有多少位就进出多少次
			// 10个空间
			// count[0] 当前位(d位)是0的数字有多少个
			// count[1] 当前位(d位)是(0和1)的数字有多少个
			// count[2] 当前位(d位)是(0,1和2)的数字有多少个
			// count[i] 当前位(d位)是(0~i)的数字有多少个
			int[] count = new int[radix]; // count[0...9]
			for(i = l; i <= r; i++) {
				j = getDigit(arr[i],d);
				count[j]++;
			}
			// 将count处理成前缀和
			for(i = 1; i <= radix; i++) {
				count[i] = count[i] + count[i - 1];
			}
			// 出桶 放到bucket中
			for(i = r; i >= l; i--) {
				j = getDigit(arr[i],d);
				bucket[count[j] - 1] = arr[i];
				count[j]--;
			}
			// 将backet中的数倒倒arr中
			for(i = l, j = 0; i <= r; i++, j++) {
				arr[i] = bucket[j];
			}
		}
	}
	//d是1 取出个位数字,d是2取出十位数字,d是3取出百位数字
	public static int getDigit(int x, int d) {
		return ((x/((int)Math.pow(10, d-1)))%10);
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值