文章目录
算法
一、算法是基本概念
1、什么是数据结构
- 存储数据的不同方式
- 例如:数组,链表
2、什么是算法
- 同一问题的不同解决方法
- 算法往往是针对特定的数据结构的
3、如何测算算法的优劣
-
时间测算
计算算法时间差
幅度不够循环来凑
-
空间测算
4、Big O标记法
-
时间复杂度
-
时间谁问题规模的变化
-
不考虑必须要做的操作
循环,赋初值,程序初始化
-
不考虑常数项
-
例如:访问数组某个位置(O(1)),访问链表某个位置(O(n))一个循环,求数组平均数(O(n))
一般时间复杂度考虑最差情况
-
用计算时间差的方式测算
5、如何写算法程序
-
由简单到复杂
验证一步走一步
多打印中间结果
-
先局部后整体
没有思路时先细分
-
先粗糙后精细
变量更名
语句合并
边界处理
二、排序算法——宋词记忆法
1、什么是排序
-
-
一般情况下最坏,最好时间复杂度,空间复杂度理解记忆
-
重要:插入,堆,归并,快速
-
宋词记忆法
选泡插,(选择,冒泡,插入)
快归堆希统计基,(快速,归并,堆,希尔,桶,计数,基数)
N方N老N一三,(选泡插时间复杂度为N2,快归堆时间复杂度为NlogN,希尔排序时间复杂度为N1.3)
对N加KN乘K,(桶排序,计数排序时间复杂度为N+K,基数排序时间复杂度为N*K)
不稳稳稳不稳稳,(依次的稳定性)
不稳不稳稳稳稳!
2、选择排序
- 最简单,但是最没用(时间复杂度高,不稳定)
程序编写
public class SelectionSort{
public static void main(String[] args){
int[] arr = {5,3,6,8,1,7,9,4,2};
for (int i = 0 ; i < arr.length - 1; i++){
int minPos = i;
int maxPos = arr.length-1;
//找到最大最小值的地方
for (int j = i+1 ; j < arr.length;j++){
minPos = arr[minPos] > arr[j] ? j : minPos;
maxPos = arr[maxPos] < arr[j] ? j : maxPos;
}
System.out.println("minPos:" + minPos);
System.out.println("maxPos:" + maxPos);
//交换
swap(arr,i,minPos);
swap(arr,arr.length-1,maxPos);
//打印
System.out.println("经过第"+ i + "次循环之后,数组的内容:");
print(arr);
System.out.println(" ");
}
}
//封装输出语句
public static void print(int[] arr){
for (int a = 0 ; a < arr.length ; a++){
System.out.print(arr[a] + " ");
}
}
//封装交换过程
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
3、冒泡排序
-
public class BubbleSort { /** * 冒泡排序 * @param a */ public static void sort(int[] a){ for (int i = 0 ; i < a.length-1 ; i++){ for (int j = 0 ; j < a.length-i-1 ; j++){ if ( a[j] > a[j+1]) swap(a,j,j+1); } } } //主函数 public static void main(String[] args) { int[] a = {5,3,6,8,1,7,9,4,2}; sort(a); print(a); } //封装输出语句 public static void print(int[] arr){ for (int a = 0 ; a < arr.length ; a++){ System.out.print(arr[a] + " "); } } //封装交换过程 public static void swap(int[] arr, int a, int b){ int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } }
4、插入排序
-
public class InsertionSort { /** * 插入排序 * @param a */ public static void sort(int[] a){ for (int i = 1 ; i < a.length ; i++){ for (int j = i ; j > 0 ; j--){ if (a[j] < a[j-1]) swap(a,j,j-1); } } } //主函数 public static void main(String[] args) { int[] a = {5,3,6,8,1,7,9,4,2}; sort(a); print(a); } //封装输出语句 public static void print(int[] arr){ for (int a = 0 ; a < arr.length ; a++){ System.out.print(arr[a] + " "); } } //封装交换过程 public static void swap(int[] arr, int a, int b){ int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } }
5、简单排序算法总结
- 冒泡排序:基本不用,太慢
- 选择排序:基本不用,不稳定
- 插入排序:样本小,基本有序的时候效率比较高,三种排序中最快的,常用。
6、希尔排序
-
改进版的插入排序
-
把一个数组按一定间隔分成好几份,每份的每个位置做插入排序,逐渐缩小间隔,再次排序,直到间隔为1
-
效率高,但是不稳定
-
Knuth序列
h=1
h=3*h+1
当h > arr.length / 3时就是最合适的间隔
之后间隔缩小时,gep = (gep -1)/3
-
public class ShellSort { /** * 希尔排序 * @param arr */ public static void sort(int[] arr) { // 处理间隔(每次缩小一倍) int h = 1; while (h <= arr.length/3){ h = h * 3 +1; } for (int gap = h; gap > 0; gap = (gap - 1)/3) { //插入排序 for (int i = gap; i < arr.length; i++) { for (int j = i; j > gap - 1; j -= gap) { if (arr[j] < arr[j - gap]) swap(arr, j, j - gap); } } } } //主函数 public static void main(String[] args) { int[] arr = {9, 6, 11, 3, 5, 12, 8, 7, 10, 15, 14, 4, 1, 13, 2}; sort(arr); print(arr); } //封装输出语句 public static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } //封装交换过程 public static void swap(int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } }
7、归并排序
递归
-
一个方法调用自己本身执行
-
static long f(int n){ if(n < 1) return -1; if(n = 1) return 1; return n + f(n-1); }
归并
-
将一个数组不断的折半,直到每个子数组不能再分,然后每个子数组进行排序,最终合并
-
public class MergeSort { public static void sort(int[] arr,int left,int right) { if (left == right) return; //分成两半 int mid = left + (right - left)/2; //如果数据过长,left+right可能越界,处理越界情况 //左边排序 sort(arr,left,mid); //右边排序 sort(arr,mid+1,right); merge(arr,left,mid+1,right); } /** * 归并排序 * * @param arr 数组arr * @param leftPtr 左指针 * @param rightPtr 右指针 * @param rightBound 右边界 */ public static void merge(int[] arr,int leftPtr,int rightPtr,int rightBound){ int[] temp = new int[rightBound-leftPtr+1]; int i = leftPtr; int j = rightPtr; int k = 0; while (i <= rightPtr-1 && j <= rightBound){ temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; } while (i<=rightPtr-1) temp[k++] = arr[i++]; while (j<=rightBound) temp[k++] = arr[j++]; for (int m=0 ; m<temp.length ; m++){ arr[leftPtr + m] = temp[m]; } } //主函数 public static void main(String[] args) { int[] arr = {9, 6, 11, 3, 5, 12, 8, 7, 10, 15, 14, 4, 1, 13, 2}; sort(arr,0,arr.length-1); print(arr); } //封装输出语句 public static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } }
8、快速排序
-
对于一个数组,找一个中轴,从前往后走,找到第一个比轴大的数,从后往前找,找到第一个小的数,两个数交换位置
-
调bug
- 通读程序
- 输出中间值
- 减功能,看最核心的部分是否有问题,然后逐渐加功能
-
轴放好之后不再改变,以轴为界分为两部分,左右各进行排序
-
public class QuickSort { public static void sort(int[] arr, int leftBound, int rightBound) { if (leftBound >= rightBound) return; int mid = partition(arr, leftBound, rightBound); //左边排序 sort(arr, leftBound, mid - 1); //右边排序 sort(arr, mid + 1, rightBound); } /** * 快速排序 * * @param arr 数组arr * @param leftBound 左边界 * @param rigthBound 右边界 * @return 轴的位置 */ public static int partition(int[] arr, int leftBound, int rigthBound) { int pivot = arr[rigthBound]; int left = leftBound; int right = rigthBound - 1; while (left <= right) { while (left <= right && arr[left] <= pivot) left++; while (left <= right && arr[right] > pivot) right--; if (left < right) swap(arr, left, right); } swap(arr, left, rigthBound); return left; } //主函数 public static void main(String[] args) { int[] arr = {9, 6, 11, 3, 5, 12, 8, 7, 10, 15, 14, 4, 1, 13, 2}; //int[] arr = {7,3,2,8,1,9,5,4,6,10,6}; sort(arr, 0, arr.length - 1); print(arr); } //封装输出语句 public static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } //封装交换过程 public static void swap(int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } }
-
双轴快排
-
以左右两个值为轴(start,end),分为左中右三部分,申请三个指针(left,right,k),k为移动指针left从start开始,left的值始终是左边值的最后一个,right的值始终是右边值的第一个,通过k的移动并于start和end比较,寻找左中右的边界,最后交换,轴放入数组中,分为三部分,各个部分递归调用函数排序。
-
public class QuickSort2 { /** * 双轴快排 * @param arr 数组arr * @param left 左边界 * @param right 右边界 */ public static void sort(int[] arr, int left, int right) { if (left > right) return; if (arr[left] > arr[right]) swap(arr, left, right); int pivot1 = arr[left]; int pivot2 = arr[right]; int l = left; int r = right; int k = left + 1; while (k < r) { if (arr[k] < pivot1) { swap(arr, ++l, k++); } else if (arr[k] > pivot1 && arr[k] <= pivot2) { k++; } else { //找到第一个比右轴小的 while (arr[r] >= pivot2) { if (r-- == k) break; } if (k >= r) break; swap(arr, k, r); } } //处理轴的位置 swap(arr, left, l); swap(arr, right, r); //小于 sort(arr,left,l-1); //中间 sort(arr,l+1,r-1); //大于 sort(arr, r+1, right); } //主函数 public static void main(String[] args) { int[] arr = {5, 3, 6, 8, 1, 7, 9, 4, 2}; sort(arr, 0, arr.length - 1); print(arr); } //封装输出语句 public static void print(int[] arr) { for (int a = 0; a < arr.length; a++) { System.out.print(arr[a] + " "); } } //封装交换过程 public static void swap(int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } }
9、计数排序
-
非比较排序
-
桶排序的一种
-
特点:适用于数量大,但是范围小的数组数据
例如:某大型企业数万名员工的年龄排序(范围:0-70)
如何快速得知高考的名次(范围0-750,数据量50W)
-
对于年龄排序问题
- 新申请一个数组长度为71的数组
- 对数据进行计数,例如,0岁有三个,则新数组0位为3,以此类推
- 最终将新数组按序展开输出
-
先找到最大最小值,获得新数组大小,每个值减去最小值,设置为其索引,放入数组,减少资源的浪费
-
如果新数组从0开始,而数据范围是[100-300],那么新数组中[0-100]属于资源浪费
-
public class CountSort { public static int[] sort(int[] arr) { //找最大最小值 int min = arr[0]; int max = arr[arr.length - 1]; for (int i = 0; i < arr.length; i++) { min = arr[i] < min ? arr[i] : min; max = arr[i] > max ? arr[i] : max; } //结果数组 int[] result = new int[arr.length]; //计数数组 int[] count = new int[max - min + 1]; //遍历arr数组,统计每个数出现的次数 for (int i = 0; i < arr.length; i++) { count[arr[i] - min]++; } //累加数组 for (int i = 1; i < count.length; i++) { count[i] += count[i - 1]; } //倒叙遍历原始数组 for (int i = arr.length - 1; i >= 0; i--) { result[--count[arr[i] - min]] = arr[i]; } return result; } //主函数 public static void main(String[] args) { int[] arr = {2, 4, 2, 3, 7, 1, 1, 5, 6, 9, 8, 5, 7, 4}; int[] result = sort(arr); print(result); } //封装输出语句 public static void print(int[] arr) { for (int a = 0; a < arr.length; a++) { System.out.print(arr[a] + " "); } } }
10、基数排序
- 非比较排序
- 本质上是多关键字的排序