算法相关
1.学习算法,最好先了解数组,链表,堆,栈等线性结构
2.对递归,遍历,算法复杂度有着常识性理解
3.主要包括三方面的知识:线性(主要是排序) — 树形结构 — 图形结构
选择排序法
定义:选择排序每次都从剩下的n - i 个元素找到最小的数,并与i位置的数交换位置。
public void selectionSort(T[] arr,int n){
for (int i = 0;i < n;i ++){
int minIndex = i;
//查找出 i+1 到 n 的最小值并放到第一位
for (int j = i + 1;j < n; j ++){
if(arr[j] .compareTo(arr[minIndex])<0){
minIndex = j;
}
}
swap(arr,i,minIndex);
}
}
选择排序法的复杂度是O(N*N) 需要对n个数进行排序,每个数的排序需要遍历(n … 1)次
插入排序法
定义:插入排序法循环n次,每次将第i + 1个数与i及其前面的数进行对比。如果i+ 1 < i 则需要进行交换。
//插入排序
public void insertSort(T[] arr,int n){
for (int i = 0;i < n;i ++){
//寻找元素arr[i]合适的插入位置
//优化每一次循环都要大量交换的问题
T e=arr[i];
int j;
for ( j = i;j>0 && arr[j-1].compareTo(e) > 0;j--){
arr[j] = arr[j-1];
}
arr[j] = e;
}
}
归并排序
定义:归并排序将一个数组进行递归二分,直到无法再细分下去后,排序并向上合并
public void mergeSort(T[] arr,int n){
mergeSort(arr,0,n-1);
}
//递归使用归并排序,对arr【l。。。r】的范围进行排序
private void mergeSort(T[] arr, int l, int r) {
//先处理递归到底的情况,即当前区间只剩下一个数的情况
if(l >= r){
return;
}
//当前区间的中间值。
int mid = l+(r - l ) / 2; //(l + r) / 2可能发生溢出
//先将mergeSort归并到 0 0
mergeSort(arr,l,mid);
//再将其归并到 0 1
mergeSort(arr,mid + 1,r);
//归并已经排序好的两部分函数,只要这样就不需要排序
if(arr[mid] .compareTo(arr[mid + 1]) > 0){
mergeSort(arr,l,mid,r);
}
}
//将arr[l...mid]和arr[mid+1...r]两部分进行归并
private void mergeSort(T[] arr, int l, int mid, int r) {
T[] aux = (T[])new Comparable[r - l + 1];
// 0 - 2
for (int i = l;i <= r;i ++){
//这边又一个i - l的偏移量 aux需要从 0 开始
aux[i - l] = arr[i];
}
//指定两个数组的开头
int i = l,j = mid + 1;
for (int k = l ; k <= r ; k ++){
if(i > mid){
//如果i已经遍历完全
arr[k] = aux[j - l];
j ++;
}else if(j > r){
//如果j已经遍历完全
arr[k] = aux[i -l];
i ++;
} else if(aux[i - l] .compareTo(aux[j - l]) < 0){
//aux需要从 0 开始
arr[k] = aux[i - l];
i ++;
}else{
arr[k] = aux[j - l];
j ++;
}
}
}
归并排序同时也可以采用自底向上的方法
public void mergeSortTwo(T[] arr,int n){
//整个数组的大小
for (int sz = 1;sz <= n;sz += sz){
//对arr[i .... i+sz-1] 和 arr[i + sz ...i+2*sz-1] 进行归并
//需要解决 i + sz -1 与 i + sz + sz -1 可能越界的问题
for (int i = 0;i + sz < n;i += sz + sz){
mergeSort(arr,i,i + sz - 1,i + sz + sz -1< n - 1?i+sz+sz-1:n - 1);
}
}
}
快速排序
定义:快速排序即随机找出一个点V,并把其他的点区分为小于V与大于V,即把一个数组分为三部分,递归排序出该数组
注意,当数组中包含大量重复键值,两边数组依然有可能极不平衡,可以采用把重复值V随机分到两边来解决这个问题
public void quickSort2(T[] arr,int n){
quickSort2(arr,0,n-1);
}
private void quickSort2(T[] arr, int l, int r) {
if(l >= r){
return;
}
int p =partition2(arr,l,r);
quickSort2(arr,l,p - 1);
quickSort2(arr,p+1,r);
}
private int partition2(T[] arr, int l, int r) {
Random random=new Random();
swap(arr,l,random.nextInt(r -l + 1) + l);
T v = arr[l];
//arr[l+1...j] <=v; arr[j...r] >= v
int i = l + 1,j = r;
while (true){
//i会停在第一个arr[i] > v 上,i从前往后遍历
while (i <= r && arr[i].compareTo(v) < 0){ i++;}
//j 会停在第一个arr[j] < v上,j从后往前遍历
while (j >= l+1 && arr[j].compareTo(v) > 0){
j -- ;
}
//判断循环是不是结束了
if(i > j){
break;
}
swap(arr,i,j);
i++;
j--;
}
swap(arr,l,j);
return j;
}
同时还有三路快速排序,分为大于v 等于v 小于v 三个部分
public void quickSort3(T[] arr,int n){
quickSort3(arr,0,n-1);
}
private void quickSort3(T[] arr, int l, int r) {
if(l >= r){
return;
}
Random random=new Random();
//随机一个数与第一个数交换,防止近乎有序的数组<V与>V两边极平衡度差
swap(arr,l,random.nextInt(r -l + 1) + l);
T v = arr[l];
int lt = l;//arr[l + 1 ... lt] <v;
int gt =r + 1;//arr[gt .. r] >v
int i = l + 1;//arr[lt+1...i] == v
while (i < gt){
if(arr[i].compareTo(v) < 0){
swap(arr,i,lt + 1);
lt ++;
i ++;
}else if(arr[i].compareTo(v) > 0){
swap(arr,i,gt - 1);
gt -- ;
}else{
i++;
}
}
swap(arr,l,lt);
quickSort3(arr,l,lt - 1);
quickSort3(arr,gt,r);
}