开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
算法
什么是算法?对一个问题做出的流程设计。
算法的分类:大体上分为两类:①明确知道怎么算的流程②明确知道怎么尝试的流程。
图灵之前的计算机都只能去进行计算,而不知道怎么尝试。图灵把“怎么试”变成了理论。
数据结构
数据结构包含类似数组那样的连续结构,还有像链表,二叉树那样的跳转结构。
位运算
我们声明一个int变量,我们看上去是一个十进制数。但他的底层是32位的二进制数。long类型为64位。而且在Java中int是有符号的。
比如我现在要打印出来数的32位是什么
👇打印底层二进制的一个算法
public static void print(int num) {
for(int i = 31;i >=0 ;i--) {
System.out.print((num & (1 << i)) == 0? "0" : "1");
}
System.out.println();
}
下面介绍一下这个算法在干嘛
刚才说了,一个int数底层是32位的,即下标从0-31。虽然我们平时打印出来的还是十进制,其实底层做了十进制转换。
既然底层是32位,那么代码中“&”运算符后面的1也是32位,即31个0最后一个是1这样的32位二进制数。
1 << i 的意思是向左移动 i 位。比如刚进去的时候 i 是31,那么这个1在底层二进制中就要向左移动31位,移动之前1呆的位置由0来补。移动31位之后就成了1后面跟了31个0
然后就是&运算符,这个运算符是只有1和1进行&得出的结果才能是1,其他都是0
结论:一个数左移一位相当于×2。即num<<1 == num*2;
反码
Q:一个int型能表示多大的范围?
A:负2的31次方 ~ 2的31次方-1
底层32位,第一位是2的0次方,一直到2的31次方。所以按理说一个int型是可以表示从0~2的32次方-1这么多数(即从2的0次方加到2的31次方),大约42亿多。但实际我们发现,Java中整型最大值(Integer.MAX_VALUE)却是21亿多。
通过打印底层32位二进制我们会看到,32位的信息中除了第一位是0其他全部是1,也就是在系统中他的32位不是都使用了。留着最高位有一个特殊含义。而真正表示值的位置其实是从0~30位。所以他表示最高是2的31次方-1这么个数
Q:这个特殊含义是什么?
int整型既可以表示正数,也可以表示负数,在Java中没有无符号整型,像C++中的无符号整型就可以表示到2的32次方-1
所以最高位我们拿来做符号位,如果此位为0,那么就是正数。如果此位为1,那么就是负数
Q:为什么负数可以到2的31次方,正数只能到2的31次方-1
A:因为把0算到正数了,0是2的0次方。算上0就是2的31次方了
如果给你一个符号位为1的32位二进制数(即负数),那么如何计算他的值呢?
很简单,除了符号位,剩下的位置状态取反再+1
比如你去打印十进制的-1的底层32位二进制,你会发现都是1。
实际上运算中的加减乘除取余都是转换成二进制进行位运算
至于为什么要取反再+1这么做,其实是为了提高运算效率,用一套逻辑执行运算。底层就不用再去追究了。
注意:系统最小取反之后是他本身,因为没有正数和他对应; 0取反是0
选择排序
比如给一个数组,要求从小到大排序。
选择排序的步骤:以N表示数组个数,0~N-1表示数组下标。
- 数组从0~N-1遍历找出最小值(记为a),把a和数组原来第一个元素对调,这样a就是在数组下标为0的位置了。
- 数组从1~N-1遍历找出最小值(记为b),把b和数组原来第二个元素对调,这样b就是在数组下标为1的位置了。
- 数组从2~N-1遍历找出最小值...... 以下是选择排序的代码(不唯一)
public class Demo01 {
public static void selectSort(int[] arr) {
if(arr == null || arr.length < 2)
return;
int N = arr.length;
// 0~N-1选最小值
// 1~N-1选最小值
// 2~N-1选最小值
// ......
for (int i = 0; i < N; i++) {
//这个i就表示找出最小值的范围
int minValueIndex = i; //记录每一次遍历最小值所在位置
for (int j = i+1; j < N; j++) { //i以后所有元素都看一遍
minValueIndex = arr[j] < arr[minValueIndex] ? j : minValueIndex; //如果j位置的元素小于所给的i位置元素就更改最小元素所在位置
}
//交换方法
swap(arr,i,minValueIndex);
}
}
public static void swap(int[] arr, int i , int j) {
int tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
public static void printArray(int[] arr) {
for(int i = 0;i<arr.length;i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int [] arr = {7,1,2,3,5,6,1,8,6,9,3};
printArray(arr);
selectSort(arr);
printArray(arr);
}
}
冒泡排序
冒泡排序的步骤:N表示数组元素,0~N-1表示下标,a表示数组名
- 给定一个乱序数组,先在a[0]和a[1]之间比较大小,谁大排后面
- a[1]和a[2]之间比较谁大谁排后面
- 一直到a[N-2]和a[N-1]比较完,“谁大谁往后”。
- 执行完上面的步骤我们就会发现最大的那个数字就排到了数组最后。到了它应该在的位置。
- 下面就是重复步骤1-3,只不过这次是从a[0]到a[N-2],因为a[N-1]已经存了最大的那个数。
public class Demo1 {
public static void swap(int[] arr, int i , int j) {
int tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
public static void bubbleSort(int[] arr) {
if(arr == null || arr.length < 2)
return;
int N = arr.length;
// 0~N-1
// 1~N-2
// 2~N-3
// ......
for(int end = N-1;end >= 0;end--) {
//0与1之间比较,1与2之间比较...end-1与end之间比较,"判断是否交换"
for(int second = 1;second <= end;second++) { //second表示比较数之间第二个数,比如0和1之间第二个数是1,1和2之间第二个数是2
if(arr[second-1]>arr[second]) {
swap(arr, second - 1, second); //交换
}
}
}
}
public static void printArray(int[] arr) {
for(int i = 0;i<arr.length;i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int [] arr = {7,1,2,3,5,6,1,8,6,9,3};
printArray(arr);
bubbleSort(arr);
printArray(arr);
}
}
插入排序
给一个无序数组,a表示数组名,N表示数组元素个数
- 从0-0保证有序(0为数组下标)
- 从0-1保证有序(即a[0]和a[1]比较大小,如果a[0]比a[1]大,则两个位置的元素互换位置)
- 从0-2保证有序(先让a[2]和a[1]比较大小,如果a[1]大于a[2],则让两个位置元素互换;再让a[1]和a[0]比较大小,如果a[0]比a[1]大,则两个位置的元素互换位置)
- ......
以下提供两种插入排序代码insertSort和insertSort1
public class Demo1 {
public static void swap(int[] arr, int i , int j) {
int tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
public static void printArray(int[] arr) {
for(int i = 0;i<arr.length;i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void insertSort(int[] arr) {
if(arr == null || arr.length < 2)
return;
int N = arr.length;
//0~0 完成
//0~1
//0~2
//...
//0~N-1
for(int end = 1;end < N;end++) {
int newNumIndex = end;
while(newNumIndex-1 >= 0 && arr[newNumIndex-1] > arr[newNumIndex]) { //end左边有数且左边的数比end位置的数大
swap(arr,newNumIndex-1,newNumIndex);
newNumIndex--; //交换之后往左移动
}
}
}
public static void insertSort1(int[] arr) {
if(arr == null || arr.length < 2)
return;
int N = arr.length;
for(int end = 1;end < N;end++) {
for(int pre = end-1;pre >= 0 && arr[pre] > arr[pre+1];pre--) {
swap(arr,pre,pre+1);
}
}
}
public static void main(String[] args) {
int [] arr = {7,1,2,3,5,6,1,8,6,9,3};
printArray(arr);
insertSort1(arr);
printArray(arr);
}
}