packageclass001;// 测试链接 : https://leetcode.cn/problems/sort-an-array/// 此时不要求掌握,因为这些排序后续的课都会讲到的// 这里只是想说明代码语言的转换并不困难// 整个系列虽然都是java讲的,但使用不同语言的同学听懂思路之后,想理解代码真的不是问题// 语言问题并不是学习算法的障碍,有了人工智能工具之后,就更不是障碍了publicclassLanguageConversion{classSolution{publicstaticint[]sortArray(int[] nums){if(nums.length >1){mergeSort(nums);}return nums;}publicstaticintMAXN=50001;// 以下是归并排序publicstaticint[] help =newint[MAXN];publicstaticvoidmergeSort(int[] arr){int n = arr.length;for(int l, m, r, step =1; step < n; step <<=1){
l =0;while(l < n){
m = l + step -1;if(m +1>= n){break;}
r =Math.min(l +(step <<1)-1, n -1);merge(arr, l, m, r);
l = r +1;}}}publicstaticvoidmerge(int[] nums,int l,int m,int r){int p1 = l;int p2 = m +1;int i = l;while(p1 <= m && p2 <= r){
help[i++]= nums[p1]<= nums[p2]? nums[p1++]: nums[p2++];}while(p1 <= m){
help[i++]= nums[p1++];}while(p2 <= r){
help[i++]= nums[p2++];}for(i = l; i <= r; i++){
nums[i]= help[i];}}// 以下是随机快速排序publicstaticvoidquickSort(int[] arr){sort(arr,0, arr.length -1);}publicstaticvoidsort(int[] arr,int l,int r){if(l >= r){return;}// 随机这一下,常数时间比较大// 但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O(n * logn)int x = arr[l +(int)(Math.random()*(r - l +1))];partition(arr, l, r, x);int left = first;int right = last;sort(arr, l, left -1);sort(arr, right +1, r);}publicstaticint first, last;publicstaticvoidpartition(int[] nums,int l,int r,int x){
first = l;
last = r;int i = l;while(i <= last){if(nums[i]== x){
i++;}elseif(nums[i]< x){swap(nums, first++, i++);}else{swap(nums, i, last--);}}}publicstaticvoidswap(int[] arr,int i,int j){int tmp = arr[i];
arr[i]= arr[j];
arr[j]= tmp;}// 以下是堆排序publicstaticvoidheapSort(int[] nums){int n = nums.length;for(int i = n -1; i >=0; i--){heapify(nums, i, n);}while(n >1){swap(nums,0,--n);heapify(nums,0, n);}}// 这个方法虽然堆排序用不上,但是堆结构里是重要方法,所以这里保留// 后面的课会讲publicstaticvoidheapInsert(int[] nums,int i){while(nums[i]> nums[(i -1)/2]){swap(nums, i,(i -1)/2);
i =(i -1)/2;}}publicstaticvoidheapify(int[] nums,int i,int s){int l = i *2+1;while(l < s){int best = l +1< s && nums[l +1]> nums[l]? l +1: l;
best = nums[best]> nums[i]? best : i;if(best == i){break;}swap(nums, best, i);
i = best;
l = i *2+1;}}}}
2. (入门)从社会实验到入门提醒
packageclass002;importjava.util.Arrays;// 一开始有100个人,每个人都有100元// 在每一轮都做如下的事情 : // 每个人都必须拿出1元钱给除自己以外的其他人,给谁完全随机// 如果某个人在这一轮的钱数为0,那么他可以不给,但是可以接收// 发生很多很多轮之后,这100人的社会财富分布很均匀吗?publicclassExperiment{publicstaticvoidmain(String[] args){System.out.println("一个社会的基尼系数是一个在0~1之间的小数");System.out.println("基尼系数为0代表所有人的财富完全一样");System.out.println("基尼系数为1代表有1个人掌握了全社会的财富");System.out.println("基尼系数越小,代表社会财富分布越均衡;越大则代表财富分布越不均衡");System.out.println("在2022年,世界各国的平均基尼系数为0.44");System.out.println("目前普遍认为,当基尼系数到达 0.5 时");System.out.println("就意味着社会贫富差距非常大,分布非常不均匀");System.out.println("社会可能会因此陷入危机,比如大量的犯罪或者经历社会动荡");System.out.println("测试开始");int n =100;int t =1000000;System.out.println("人数 : "+ n);System.out.println("轮数 : "+ t);experiment(n, t);System.out.println("测试结束");}// 完全按照说的来实验publicstaticvoidexperiment(int n,int t){double[] wealth =newdouble[n];Arrays.fill(wealth,100);boolean[] hasMoney =newboolean[n];for(int i =0; i < t; i++){Arrays.fill(hasMoney,false);for(int j =0; j < n; j++){if(wealth[j]>0){
hasMoney[j]=true;}}for(int j =0; j < n; j++){if(hasMoney[j]){int other = j;do{// (int) (Math.random() * n);// int : 0 ~ n-1,等概率随机
other =(int)(Math.random()* n);}while(other == j);
wealth[j]--;
wealth[other]++;}}}Arrays.sort(wealth);System.out.println("列出每个人的财富(贫穷到富有) : ");for(int i =0; i < n; i++){System.out.print((int) wealth[i]+" ");if(i %10==9){System.out.println();}}System.out.println();System.out.println("这个社会的基尼系数为 : "+calculateGini(wealth));}// 计算基尼系数// 看代码就可以轻易知道怎么算的publicstaticdoublecalculateGini(double[] wealth){double sumOfAbsoluteDifferences =0;double sumOfWealth =0;int n = wealth.length;for(int i =0; i < n; i++){
sumOfWealth += wealth[i];for(int j =0; j < n; j++){
sumOfAbsoluteDifferences +=Math.abs(wealth[i]- wealth[j]);}}return sumOfAbsoluteDifferences /(2* n * sumOfWealth);}}
3. (入门)二进制与位运算
packageclass003;// 本文件的实现是用int来举例的// 对于long类型完全同理// 不过要注意,如果是long类型的数字num,有64位// num & (1 << 48),这种写法不对// 因为1是一个int类型,只有32位,所以(1 << 48)早就溢出了,所以无意义// 应该写成 : num & (1L << 48)publicclassBinarySystem{// 打印一个int类型的数字,32位进制的状态// 左侧是高位,右侧是低位publicstaticvoidprintBinary(int num){for(int i =31; i >=0; i--){// 下面这句写法,可以改成 :// System.out.print((a & (1 << i)) != 0 ? "1" : "0");// 但不可以改成 :// System.out.print((a & (1 << i)) == 1 ? "1" : "0");// 因为a如果第i位有1,那么(a & (1 << i))是2的i次方,而不一定是1// 比如,a = 0010011// a的第0位是1,第1位是1,第4位是1// (a & (1<<4)) == 16(不是1),说明a的第4位是1状态System.out.print((num &(1<< i))==0?"0":"1");}System.out.println();}publicstaticvoidmain(String[] args){// 非负数int a =78;System.out.println(a);printBinary(a);System.out.println("===a===");// 负数int b =-6;System.out.println(b);printBinary(b);System.out.println("===b===");// 直接写二进制的形式定义变量int c =0b1001110;System.out.println(c);printBinary(c);System.out.println("===c===");// 直接写十六进制的形式定义变量// 0100 -> 4// 1110 -> e// 0x4e -> 01001110int d =0x4e;System.out.println(d);printBinary(d);System.out.println("===d===");// ~、相反数System.out.println(a);printBinary(a);printBinary(~a);int e =~a +1;System.out.println(e);printBinary(e);System.out.println("===e===");// int、long的最小值,取相反数、绝对值,都是自己int f =Integer.MIN_VALUE;System.out.println(f);printBinary(f);System.out.println(-f);printBinary(-f);System.out.println(~f +1);printBinary(~f +1);System.out.println("===f===");// | & ^int g =0b0001010;int h =0b0001100;printBinary(g | h);printBinary(g & h);printBinary(g ^ h);System.out.println("===g、h===");// 可以这么写 : int num = 3231 | 6434;// 可以这么写 : int num = 3231 & 6434;// 不能这么写 : int num = 3231 || 6434;// 不能这么写 : int num = 3231 && 6434;// 因为 ||、&& 是 逻辑或、逻辑与,只能连接boolean类型// 不仅如此,|、& 连接的两侧一定都会计算// 而 ||、&& 有穿透性的特点System.out.println("test1测试开始");boolean test1 =returnTrue()|returnFalse();System.out.println("test1结果,"+ test1);System.out.println("test2测试开始");boolean test2 =returnTrue()||returnFalse();System.out.println("test2结果,"+ test2);System.out.println("test3测试开始");boolean test3 =returnFalse()&returnTrue();System.out.println("test3结果,"+ test3);System.out.println("test4测试开始");boolean test4 =returnFalse()&&returnTrue();System.out.println("test4结果,"+ test4);System.out.println("===|、&、||、&&===");// <<int i =0b0011010;printBinary(i);printBinary(i <<1);printBinary(i <<2);printBinary(i <<3);System.out.println("===i << ===");// 非负数 >> >>>,效果一样printBinary(i);printBinary(i >>2);printBinary(i >>>2);System.out.println("===i >> >>>===");// 负数 >> >>>,效果不一样int j =0b11110000000000000000000000000000;printBinary(j);printBinary(j >>2);printBinary(j >>>2);System.out.println("===j >> >>>===");// 非负数 << 1,等同于乘以2// 非负数 << 2,等同于乘以4// 非负数 << 3,等同于乘以8// 非负数 << i,等同于乘以2的i次方// ...// 非负数 >> 1,等同于除以2// 非负数 >> 2,等同于除以4// 非负数 >> 3,等同于除以8// 非负数 >> i,等同于除以2的i次方// 只有非负数符合这个特征,负数不要用int k =10;System.out.println(k);System.out.println(k <<1);System.out.println(k <<2);System.out.println(k <<3);System.out.println(k >>1);System.out.println(k >>2);System.out.println(k >>3);System.out.println("===k===");}publicstaticbooleanreturnTrue(){System.out.println("进入了returnTrue函数");returntrue;}publicstaticbooleanreturnFalse(){System.out.println("进入了returnFalse函数");returnfalse;}}
4. (入门)选择冒泡插入排序
packageclass004;publicclassSelectBubbleInsert{// 数组中交换i和j位置的数publicstaticvoidswap(int[] arr,int i,int j){int tmp = arr[i];
arr[i]= arr[j];
arr[j]= tmp;}// 选择排序publicstaticvoidselectionSort(int[] arr){if(arr ==null|| arr.length <2){return;}for(int minIndex, i =0; i < arr.length -1; i++){
minIndex = i;for(int j = i +1; j < arr.length; j++){if(arr[j]< arr[minIndex]){
minIndex = j;}}swap(arr, i, minIndex);}}// 冒泡排序publicstaticvoidbubbleSort(int[] arr){if(arr ==null|| arr.length <2){return;}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);}}}}// 插入排序publicstaticvoidinsertionSort(int[] arr){if(arr ==null|| arr.length <2){return;}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);}}}}
5. (入门)对数器
packageclass005;publicclassValidator{// 为了验证publicstaticvoidmain(String[] args){// 随机数组最大长度intN=200;// 随机数组每个值,在1~V之间等概率随机intV=1000;// testTimes : 测试次数int testTimes =50000;System.out.println("测试开始");for(int i =0; i < testTimes; i++){// 随机得到一个长度,长度在[0~N-1]int n =(int)(Math.random()*N);// 得到随机数组int[] arr =randomArray(n,V);int[] arr1 =copyArray(arr);int[] arr2 =copyArray(arr);int[] arr3 =copyArray(arr);selectionSort(arr1);bubbleSort(arr2);insertionSort(arr3);if(!sameArray(arr1, arr2)||!sameArray(arr1, arr3)){System.out.println("出错了!");// 当有错了// 打印是什么例子,出错的// 打印三个功能,各自排序成了什么样// 可能要把例子带入,每个方法,去debug!}}System.out.println("测试结束");}// 为了验证// 得到一个随机数组,长度是n// 数组中每个数,都在1~v之间,随机得到publicstaticint[]randomArray(int n,int v){int[] arr =newint[n];for(int i =0; i < n; i++){// Math.random() -> double -> [0,1)范围山的一个小数,0.37463473126、0.001231231,等概率!// Math.random() * v -> double -> [0,v)一个小数,依然等概率// (int)(Math.random() * v) -> int -> 0 1 2 3 ... v-1,等概率的!// (int) (Math.random() * v) + 1 -> int -> 1 2 3 .... v,等概率的!
arr[i]=(int)(Math.random()* v)+1;}return arr;}// 为了验证publicstaticint[]copyArray(int[] arr){int n = arr.length;int[] ans =newint[n];for(int i =0; i < n; i++){
ans[i]= arr[i];}return ans;}// 为了验证publicstaticbooleansameArray(int[] arr1,int[] arr2){int n = arr1.length;for(int i =0; i < n; i++){if(arr1[i]!= arr2[i]){returnfalse;}}returntrue;}// 数组中交换i和j位置的数publicstaticvoidswap(int[] arr,int i,int j){int tmp = arr[i];
arr[i]= arr[j];
arr[j]= tmp;}// 选择排序publicstaticvoidselectionSort(int[] arr){if(arr ==null|| arr.length <2){return;}for(int minIndex, i =0; i < arr.length -1; i++){
minIndex = i;for(int j = i +1; j < arr.length; j++){if(arr[j]< arr[minIndex]){
minIndex = j;}}swap(arr, i, minIndex);}}// 冒泡排序publicstaticvoidbubbleSort(int[] arr){if(arr ==null|| arr.length <2){return;}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);}}}}// 插入排序publicstaticvoidinsertionSort(int[] arr){if(arr ==null|| arr.length <2){return;}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);}}}}
6. (入门)二分搜索
packageclass006;importjava.util.Arrays;// 有序数组中是否存在一个数字publicclassCode01_FindNumber{// 为了验证publicstaticvoidmain(String[] args){intN=100;intV=1000;int testTime =500000;System.out.println("测试开始");for(int i =0; i < testTime; i++){int n =(int)(Math.random()*N);int[] arr =randomArray(n,V);Arrays.sort(arr);int num =(int)(Math.random()*V);if(right(arr, num)!=exist(arr, num)){System.out.println("出错了!");}}System.out.println("测试结束");}// 为了验证publicstaticint[]randomArray(int n,int v){int[] arr =newint[n];for(int i =0; i < n; i++){
arr[i]=(int)(Math.random()* v)+1;}return arr;}// 为了验证// 保证arr有序,才能用这个方法publicstaticbooleanright(int[] sortedArr,int num){for(int cur : sortedArr){if(cur == num){returntrue;}}returnfalse;}// 保证arr有序,才能用这个方法publicstaticbooleanexist(int[] arr,int num){if(arr ==null|| arr.length ==0){returnfalse;}int l =0, r = arr.length -1, m =0;while(l <= r){
m =(l + r)/2;if(arr[m]== num){returntrue;}elseif(arr[m]> num){
r = m -1;}else{
l = m +1;}}returnfalse;}}
packageclass006;importjava.util.Arrays;// 有序数组中找>=num的最左位置publicclassCode02_FindLeft{// 为了验证publicstaticvoidmain(String[] args){intN=100;intV=1000;int testTime =500000;System.out.println("测试开始");for(int i =0; i < testTime; i++){int n =(int)(Math.random()*N);int[] arr =randomArray(n,V);Arrays.sort(arr);int num =(int)(Math.random()*N);if(right(arr, num)!=findLeft(arr, num)){System.out.println("出错了!");}}System.out.println("测试结束");}// 为了验证publicstaticint[]randomArray(int n,int v){int[] arr =newint[n];for(int i =0; i < n; i++){
arr[i]=(int)(Math.random()* v)+1;}return arr;}// 为了验证// 保证arr有序,才能用这个方法publicstaticintright(int[] arr,int num){for(int i =0; i < arr.length; i++){if(arr[i]>= num){return i;}}return-1;}// 保证arr有序,才能用这个方法// 有序数组中找>=num的最左位置publicstaticintfindLeft(int[] arr,int num){int l =0, r = arr.length -1, m =0;int ans =-1;while(l <= r){// m = (l + r) / 2;// m = l + (r - l) / 2;
m = l +((r - l)>>1);if(arr[m]>= num){
ans = m;
r = m -1;}else{
l = m +1;}}return ans;}}
packageclass006;importjava.util.Arrays;// 有序数组中找<=num的最右位置publicclassCode03_FindRight{// 为了验证publicstaticvoidmain(String[] args){intN=100;intV=1000;int testTime =500000;System.out.println("测试开始");for(int i =0; i < testTime; i++){int n =(int)(Math.random()*N);int[] arr =randomArray(n,V);Arrays.sort(arr);int num =(int)(Math.random()*N);if(right(arr, num)!=findRight(arr, num)){System.out.println("出错了!");}}System.out.println("测试结束");}// 为了验证publicstaticint[]randomArray(int n,int v){int[] arr =newint[n];for(int i =0; i < n; i++){
arr[i]=(int)(Math.random()* v)+1;}return arr;}// 为了验证// 保证arr有序,才能用这个方法publicstaticintright(int[] arr,int num){for(int i = arr.length -1; i >=0; i--){if(arr[i]<= num){return i;}}return-1;}// 保证arr有序,才能用这个方法// 有序数组中找<=num的最右位置publicstaticintfindRight(int[] arr,int num){int l =0, r = arr.length -1, m =0;int ans =-1;while(l <= r){
m = l +((r - l)>>1);if(arr[m]<= num){
ans = m;
l = m +1;}else{
r = m -1;}}return ans;}}
packageclass006;// 峰值元素是指其值严格大于左右相邻值的元素// 给你一个整数数组 nums,已知任何两个相邻的值都不相等// 找到峰值元素并返回其索引// 数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。// 你可以假设 nums[-1] = nums[n] = 无穷小// 你必须实现时间复杂度为 O(log n) 的算法来解决此问题。publicclassCode04_FindPeakElement{// 测试链接 : https://leetcode.cn/problems/find-peak-element/classSolution{publicstaticintfindPeakElement(int[] arr){int n = arr.length;if(arr.length ==1){return0;}if(arr[0]> arr[1]){return0;}if(arr[n -1]> arr[n -2]){return n -1;}int l =1, r = n -2, m =0, ans =-1;while(l <= r){
m =(l + r)/2;if(arr[m -1]> arr[m]){
r = m -1;}elseif(arr[m]< arr[m +1]){
l = m +1;}else{
ans = m;break;}}return ans;}}}
7. (入门)时间复杂度与空间复杂度
packageclass007;importjava.util.ArrayList;publicclassComplexity{// 只用一个循环完成冒泡排序// 但这是时间复杂度O(N^2)的!publicstaticvoidbubbleSort(int[] arr){if(arr ==null|| arr.length <2){return;}int n = arr.length;int end = n -1, i =0;while(end >0){if(arr[i]> arr[i +1]){swap(arr, i, i +1);}if(i < end -1){
i++;}else{
end--;
i =0;}}}publicstaticvoidswap(int[] arr,int i,int j){int tmp = arr[i];
arr[i]= arr[j];
arr[j]= tmp;}publicstaticvoidmain(String[] args){// 随机生成长度为n// 值在0~v-1之间// 且任意相邻两数不相等的数组int n =10;int v =4;int[] arr1 =newint[n];
arr1[0]=(int)(Math.random()* v);for(int i =1; i < n; i++){do{
arr1[i]=(int)(Math.random()* v);}while(arr1[i]== arr1[i -1]);}for(int num : arr1){System.out.print(num +" ");}System.out.println();System.out.println("=========");// java中的动态数组是ArrayList// 各个语言中的动态数组的初始大小和实际扩容因子可能会变化,但是均摊都是O(1)// 课上用2作为扩容因子只是举例而已ArrayList<Integer> arr2 =newArrayList<>();
arr2.add(5);// 0
arr2.add(4);// 1
arr2.add(9);// 2
arr2.set(1,6);// arr[1]由4改成了6System.out.println(arr2.get(1));System.out.println("=========");int[] arr ={64,31,78,0,5,7,103};bubbleSort(arr);for(int num : arr){System.out.print(num +" ");}System.out.println();System.out.println("=========");intN=200000;long start;long end;System.out.println("测试开始");
start =System.currentTimeMillis();for(int i =1; i <=N; i++){for(int j = i; j <=N; j += i){// 这两个嵌套for循环的流程,时间复杂度为O(N * logN)// 1/1 + 1/2 + 1/3 + 1/4 + 1/5 + ... + 1/n,也叫"调和级数",收敛于O(logN)// 所以如果一个流程的表达式 : n/1 + n/2 + n/3 + ... + n/n// 那么这个流程时间复杂度O(N * logN)}}
end =System.currentTimeMillis();System.out.println("测试结束,运行时间 : "+(end - start)+" 毫秒");System.out.println("测试开始");
start =System.currentTimeMillis();for(int i =1; i <=N; i++){for(int j = i; j <=N; j++){// 这两个嵌套for循环的流程,时间复杂度为O(N^2)// 很明显等差数列}}
end =System.currentTimeMillis();System.out.println("测试结束,运行时间 : "+(end - start)+" 毫秒");}}
8. (入门)算法与数据结构简介
9. (入门)单双链表及其反转-堆栈诠释
packageclass009;// 按值传递、按引用传递// 从堆栈角度解释链表节点// 以堆栈视角来看链表反转publicclassListReverse{publicstaticvoidmain(String[] args){// int、long、byte、short// char、float、double、boolean// 还有String// 都是按值传递int a =10;f(a);System.out.println(a);// 其他类型按引用传递// 比如下面的Number是自定义的类Number b =newNumber(5);g1(b);System.out.println(b.val);g2(b);System.out.println(b.val);// 比如下面的一维数组int[] c ={1,2,3,4};g3(c);System.out.println(c[0]);g4(c);System.out.println(c[0]);}publicstaticvoidf(int a){
a =0;}publicstaticclassNumber{publicint val;publicNumber(int v){
val = v;}}publicstaticvoidg1(Number b){
b =null;}publicstaticvoidg2(Number b){
b.val =6;}publicstaticvoidg3(int[] c){
c =null;}publicstaticvoidg4(int[] c){
c[0]=100;}// 单链表节点publicstaticclassListNode{publicint val;publicListNode next;publicListNode(int val){this.val = val;}publicListNode(int val,ListNode next){this.val = val;this.next = next;}}// 反转单链表测试链接 : https://leetcode.cn/problems/reverse-linked-list/classSolution{publicstaticListNodereverseList(ListNode head){ListNode pre =null;ListNode next =null;while(head !=null){
next = head.next;
head.next = pre;
pre = head;
head = next;}return pre;}}// 双链表节点publicstaticclassDoubleListNode{publicint value;publicDoubleListNode last;publicDoubleListNode next;publicDoubleListNode(int v){
value = v;}}// 反转双链表// 没有找到测试链接// 如下方法是对的publicstaticDoubleListNodereverseDoubleList(DoubleListNode head){DoubleListNode pre =null;DoubleListNode next =null;while(head !=null){
next = head.next;
head.next = pre;
head.last = next;
pre = head;
head = next;}return pre;}}