算法笔记1~3章

第一章

冒泡排序

	 public static void bubbleSort(int[] arr) {
        if (arr == null || arr.length < 2) 		return;
        
        for (int e = arr.length - 1; e > 0; e--) {
            for (int i = 0; i < e; i++) {//最大的浮动到当前的右边界
                if (arr[i] > arr[i + 1]) 
                    swap(arr, i, i + 1);
            }
        }
    }	

选择排序

    public static void selectionSort(int[] arr) {
        if (arr == null || arr.length < 2) 		return;
        
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[j] < arr[minIndex] ? j : minIndex;
            }
            swap(arr, i, minIndex);
        }
    }
 

插入排序

    public static void insertionSort(int[] arr) {
        if (arr == null || arr.length < 2) 		return;
        
        for (int i = 1; i < arr.length; i++) {	//	i-1前的已经有序
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {// arr[i]不断和前面的比较,
                swap(arr, j, j + 1);
            }
        }
    }
 
    public static void swap(int[] arr, int i, int j) {
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
 

Master定理

子问题规模等量, 才能使用
在这里插入图片描述

二分 使用master定理
a:调用次数;2
N/b:调用规模;N/2
除了子问题之外剩下的时间复杂度:O(1)
因此, T(N) = 2T(N/2)+O(1);

第二章 O(NlogN)的排序算法

归并排序 问题分析

在这里插入图片描述
小和问题(和归并排序一样,但是同时求小和)

分析: 求一个数左边比他小的数之和,其实就是求一个数右边比他大的数的个数,再乘以原数字。
在这里插入图片描述
res 是小和,(1)当右边p2值比p1的大时,说明p2之后的值也比p1的大,因此一共有r-p2+1个数比p1大。 (2)如果p1的值较大则不需要加,即+0。
辅助数组是<而不是<=,保证了相等情况下右边先入。
在这里插入图片描述
左小和 + 右小和 +归并时的小和

求一个数右边有 多少个数 比当前数大,把 个数n 乘以这个数。
在这里插入图片描述
merge时,左右组有元素相等,右组先拷贝且不产生小和,则保证了快速得到个数。
在这里插入图片描述
逆序对问题
求一个数右边有 多少个数 比他小,和上面一样。其实就是求一个数,左边有多少个数比他大。

这两个问题的关键都是,转化问题说法,因为归并的性质是小的先入,所以让找 哪边有多少数比他大较好,可以直接用 右边界-当前坐标+1 得出比他大的个数(都是递增的)。

num += arr[p2] < arr[p1] ? m-p1+1 : 0;
help[i++] = arr[p1] < arr[p2] arr[p1] : arrp2]; //相等左边先入 

快排

在这里插入图片描述
快排1.0
普通快排
快排2.0
适合key有很多重复值

[2,3,4,4,4,5]
p[0]=2, p[1]=4
相比1.0, 可以不用管中间的了

public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//随机选择key,然后和最后一个交换,然后继续
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);  <区
			quickSort(arr, p[1] + 1, r); >区
		}
	}
public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;//小于区
		int more = r;//大于区
		while (l < more) {
			if (arr[l] < arr[r]) {
				swap(arr, ++less, l++);
			} else if (arr[l] > arr[r]) {
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		swap(arr, more, r);
		return new int[] { less + 1, more };
	}

选最后一个为key,数字和key一样时候,右边第一个和 末尾的key换。
快排3.0
随机选择key,不然如果是1,2,3,4,5,6,7,8,9这样的数组时间复杂度就是n^2了。

堆排序

完全二叉树
完全二叉树,用数组表示
左子树 2\*i+1;
右子树 2\*i+2;
父节点 (i-1)/2;

大顶堆

  1. heapInsert

    a. 新来的不断和父 pk,大了就上去。O(logN)树高logN
    b. 到了顶部时,自己不比自己大就结束,一个while搞定
    heapInsert
    注 : 可用于往堆里添加元素:
    (1)把key加到末尾。
    (2)index=heapSize-1;
    (3)往上heapInsert;

  2. 弹出堆顶元素 heapify
    a. 把最后一个换到堆顶,heapsize–;
    b. 然后不断和自己俩孩子的最大值pk,小了就下去(交换)。O(logN)树高logN
    heapify
    注 : 可用于弹出堆顶元素:
    (1)把最后一个换到堆顶,heapSize–;
    (2)index=0,往下heapify。

题目1:给定一个堆,把i位置的数改一下。
分析:
1. 知道变大变小
变小了,往下heapify
变大了,往上heapInsert

2. 不知道大小
先heapInsert,①能,就完事;
②不能就会停, 然后再heapify。

堆排序
得到大顶堆后:

  1. 把堆顶和末尾换,heapsize–;
  2. heapify,得到子 大顶堆
  3. 循环到heapsize==0;
    注: 就是先构建顶堆,然后不断弹出的过程。每一个最大值到了末尾,最后数组就是升序的
    在这里插入图片描述

如果给一堆数构建顶堆时
1.可以不从第一个heapInsert,O(N logN)
2.也可以从最后一个进行heapify。O(N)

额外空间复杂度
堆排序:O(1)
归并排序:O(N)
快速排序:O(logN)

快速排序, 即记录中点开辟的内存。 左边用完释放右边用,因此整个是 树高O(logN)级别的。 但是如果每次选的值都在最右侧,[1,2,3,4,5]那么额外空间复杂度就是O(N)
快排空间复杂度

题目二

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

分析:
因为是几乎有序,所以0~6必然存在最小值。
(因为最小值应该在0处,最大从6处挪到0处,一共用6步)
0-6放到小顶堆,因为如果0在6位之后,0要移动的位数大于k=6了,0必然在0-6位。
那么每次弹出堆顶放到最前面,再加入新元素就会有序。(弹出之后是仍是小根堆,heapsize=5,。然后让7入堆,heapsize=6。循环)
在这里插入图片描述
code

第三章 桶排序,排序总结

比较器

即,C++比较符重载
比较器
Arrays. sort(students, new IdAscendingComparator());//小根堆
大根堆同理。

桶排序

桶排序思想下的排序
1)计数排序
2)基数排序

桶排序思想下的排序都是不基于比较的排序
2) 时间复杂度为O(N),额外空间负载度O(M)
3)应用范围有限,需要样本的数据状况满足桶的划分

计数排序

是一种O(n)的排序算法,其思路是开一个长度为 maxValue-minValue+1 的数组,然后

  1. 分配。扫描一遍原始数组,以当前值- minValue 作为下标,将该下标的计数器增1。
  2. 收集。扫描一遍计数器数组,按顺序把值收集起来。

举个例子, nums=[2, 1, 3, 1, 5] , 首先扫描一遍获取最小值和最大值, maxValue=5 , minValue=1 ,于是开一个长度为5的计数器数组 counter.

  1. 分配。统计每个元素出现的频率,得到 counter=[2, 1, 1, 0, 1] ,例如 counter[0] 表示值 0+minValue=1 出现了2次。
  2. 收集。 counter[0]=2 表示 1 出现了两次,那就向原始数组写入两个1, counter[1]=1 表示 2 出现了1次,那就向原始数组写入一个2,依次类推,最终原始数组变为 [1,1,2,3,5] ,排序好了。

计数排序本质上是一种特殊的桶排序,当桶的个数最大的时候,就是计数排序。

基数排序

  1. 准备10个桶,从个位开始,(先进先出)
    在这里插入图片描述
    然后倒出来,如下图数组

  2. 再次, 十位进行操作
    在这里插入图片描述

  3. 再次, 百位进行操作
    在这里插入图片描述

  4. 再次倒出得到[13, 17, 25, 72, 100]
    注: 可以用栈实现

Code: (不用栈实现)
radixSort

基数排序的另一种方法,不用栈
count[] 为当先位数<=i的个数。 在个位时, <=1只有21,11 (2个); <=2 有21,11,52,65 (4个); <=3多一个13(5个)。

help[] 调整后的数组,从右往左开始。
62, 2的词频为4,help[4-1] = 62, count[4]–;
同理, 52, 2的词频变为了3,help[3-1] = 52, count[3]–;
同理, 11, 1的词频变为了2,help[2-1] = 11, count[2]–;
最终个位完成就是[21, 11, 52, 62, 13 ]
循环。

稳定性

Define: 排序后 值相同的数 相对次序不变

在这里插入图片描述
在这里插入图片描述
注:

  1. 选择排序[3,3,3,1,3,3,3], 1和第一个3交换。不稳定。
  2. 冒泡排序[2,2,1,2,2], 值相等不交换就可以稳定。
  3. 插入排序[3,2,2], 值相等不交换就可以稳定。
  4. 归并排序[1,1,2,3]----[1,2,3], 值相等,左边先入就可以稳定。小和问题稳定性丧失。
  5. 快速排序[6,7,6,6,3], key=5, 3和第一个6换,不稳定。常数项最低,一般先选
  6. 堆排序, 不稳定。
    排序比较
    常见的坑

//space

//space

//space

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值