各种繁杂,但总是容易忽视,优秀的代码碎块。
优秀的算法总是让人惊艳,到底是何种大牛,何等想法,竟然能实现了如此的数字或是字符处理,更是能实现了那种总是正常,但是仔细想来却是匪夷所思的目的。
但是往往优秀的算法的根基,有一些则是是算法碎块的堆砌,在真正踏上了算法学习的道路上,我们还需要准备些真正的有用,实在的工具。
而不让我们在追求算法的道路上,遭遇拦路虎,而无工具,只能赤手空拳,绞尽脑汁去硬拼,那可不安全咯。
为此,特地做一部分的算法碎块整理,以便能在记忆过后,能对应相应的算法想到了解决方法,从而一块块的处理得到完整完好的解答。
本文涉及内容:
1.交换两个数字位置的两种方法
2.返回整数的符号(正数或0返回1,负数返回0)
3.常见排序重要的实现代码块
4.归并排序中合并核心代码块
***引入知识:如何去估计判断一个递归的复杂式子?
5.对于某一个数而言,快速将一组数分成了左边是小于该数的一组,中间是相等该数,右边是大于该数的一组(荷兰国旗问题,注意不保证有序性和稳定性)(可以引申到快速排序哦!)
6.快速排序核心代码块(其实就是上述的荷兰国旗的引申,不过有个地方要变,那就是num的位置,是变成数组的最后一个数字)
7.堆排序(非常重要,所以整体整理记忆)
8.使用有限队列呈现大根堆和小根堆
9.逆序一个非负数(逆序后首部不包含0,也即正常的数字)
10.桶排序(例题讲评)
代码块一 --交换两个数字位置的两种方法
//交换i j 位置的值,引入一个临时变量
//交换数组中两个位置的值,这里传入的是下标指针
int i=3,j=4;
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = arr[i];
================================================================
//不需要引入临时变量,
arr[i] = arr[i]^arr[j];
arr[j] = arr[i]^arr[j];
arr[i] = arr[i]^arr[j];
===============================================================
//不使用临时变量的另一种做法
arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];
代码块二 --返回整数的符号(正数或0返回1,负数返回0)
public static int sign(int n) {
return ((n >> 31) & 1) ^ 1;
}
代码块三 -- 常见排序重要的实现块
int[] arr= {1,56,98,12,45,32,61,5,9,10};//example
//冒泡排序中核心实现块
for(int end = arr.length-1 ;end > 0; end--) {
for(int i = 0 ; i <end ;i++) {
if (arr[i] > arr[i+1]) {
swap(arr,i,i+1);
}
}
}
int[] arr= {1,56,98,12,45,32,61,5,9,10};//example
//选择排序核心块
for(int i = 0 ;i < arr.length;i++) {
int minIndex = i;
//选择出该后续子数组中的最小值与当前值进行比对选择交换
for(int j = i+1 ;j <arr.length;j++) {
minIndex = arr[j]<arr[minIndex]? j:minIndex;
}
swap(arr, i, minIndex);
}
int[] arr= {1,56,98,12,45,32,61,5,9,10};//example
//插入排序核心代码块(重要)
for(int i = 1; i< arr.length ;i++) {
for(int j = i-1 ;j>=0 && arr[j]>arr[j+1];j--) {
swap(arr, j, j+1);
}
}
//tips:大家可以对比下和选择排序略有不同的是,插入排序只是一次移动一个位置,然后在已经指针移动过了的的位置中,进行比对排序;而对于选择排序而言,它确实移动之后只去看当前位置和后面数组中的数字去比对。
代码块四 --归并排序中合并块
public static void merge(int[] arr,int L ,int mid, int R) {
//L代表数组的最左的位置,R表示数组最右的位置,mid表示要排序的数组分割的重点
int[] help = new int[R-L+1];
int i=0;//记录辅助数组的下标
int p1=L;//记录左分组的下标
int p2=mid+1;//记录右分组的下标,注意是从mid+1开始
while(p1 <= mid && p2<= R) {
help[i++] = arr[p1] <arr[p2] ? arr[p1++]:arr[p2++];
}
//两个分组当中,有且只有一组越界
while(p1<=mid) {
help[i++] = arr[p1++];
}
while(p2<=mid) {
help[i++] = arr[p2++];
}
//此款的辅助数组已经将已经排序过的左右分组全部按顺序排好序了,
//只要在覆盖掉原有的数组就完成了归并排序的最重要的部分
//tips:i在前面已经做为下标出现过的变量,此处可以继续引用
for(i = 0 ;i <help.length;i++) {
arr[L+i]=help[i];
}
}
***引入知识:如何去估计判断一个递归的复杂式子?
由公式
,
其中T(N)表示样本量为N的时间复杂度;:子过程的样本量(b是代表分成了几分之几);a表示的是子过程发生的次数;表示
除去了用子过程之外的剩下用去了多少空间或者说空间复杂度.
且有
代码块五 --对于某一个数而言,快速将一组数分成了左边是小于该数的一组,中间是相等该数,右边是大于该数的一组(荷兰国旗问题,注意不保证有序性和稳定性)(可以引申到快速排序哦!)
public static int[] partition(int[] arr, int L ,int R,int num) {
//返回的数组是一个二元数组,表示了分割的位置,分别就是小于num以及大于num的位置
//得到位置的同时,将数组进行了错分,分成了小于num的左边一组,等于num中间一组,大于num的右边一组
int less = L - 1;//less表示小于num的位置下标
int more = R + 1; //more表示大于num的位置下标处
//tips:在这里,将less和more分别都是从界外开始
while (L < more) {
if (arr[L] < num) {
//如果一开始某个数小于num,将某个为和L交换
//(一开始是自己和自己交换)
swap(arr,++less,L++);
}else if (arr[L]>num) {
//如果某个数大于num,则和最右边的数进行交换
swap(arr,--more,L);
}else {
//如果不是大于也不是小于num则移动L下标
//这里是复用了L作为了数组下标索引
L++;
}
}
return new int[]{less+1, more-1};
}
代码块六 --快速排序核心代码块(其实就是上述的荷兰国旗的引申,不过有个地方要变,那就是num的位置,是变成数组的最后一个数字)
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};
}
代码块 七 -- 堆排序(非常重要,所以整体整理记忆)
Tips:“优先队列”就是堆解构
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);//这里存储0~i的大根堆
}
int size = arr.length;
swap(arr, 0, --size);
//请思考,为何这里还要执行以下的操作?
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
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 size) {
int left = index * 2 + 1;
while (left < size) {
int largest = left + 1 < size && 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 swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
代码块八 --使用有限队列呈现大根堆和小根堆
//小根堆
public static class minHeapComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
public static void main(String[] args) {
PriorityQueue<Integer> minHeap = new PriorityQueue<>(new minHeapComparator());
//......
}
//大根堆
public static class maxHeapComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2 - o1 ;
}
}
public static void main(String[] args) {
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new maxHeapComparator());
//......
}
代码块 九-- 逆序一个非负数(不包含0)
int res = 0;//OriginalNum 表示原始数
while (OriginalNum != 0) {
res=OriginalNum%10 +10*res;
OriginalNum=OriginalNum/10;
}
代码十 --桶排序(由于样排序较少使用,结合具体的一道代码题进行解释)
题目:给定一个数组,求如果排序之后,相邻的两个数的最大差值。要求时间复杂度为O(N),且要求不能用非基于比较的排序。
思路:使用桶排序 N个数,N+1一个桶,根据鸽巢原理,必有一个桶将是空桶。
那么我们只要将所有的数字进行桶的排序,然后扫描所有桶顶的数比较,只要比之差值最大,就是我们找的最大差值。
桶顶数的规则就是,如果当原先的桶中没有数,但是遍历数组的数分到了这个桶,将之放进去。每个桶只保留两个数,就是要么最大,要么最小。也即每次遍历差值,只找前一个的最大值,和后一个的最小值,那么就是最大的差值,而且无遗漏。
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]);
}
//如果间隔差值为0,毫无比较意义
if (min == max) {
return 0;
}
//定义数组 N+1个桶
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));
}
未完待续哦~!(*❦ω❦)后续将继续更新总结~
本文为博主原创总结更新,谢绝转载,本文博客地址为:https://blog.youkuaiyun.com/mukvintt/article/details/81611184