排序算法
排序算法,字面意思上来说,就是用来排序的算法,我主要从简单到复杂介绍几个常见的排序。
首先引入两个问题,给我们两个已经排好序的数组和链表我们要怎么把他们合并成一个数组或者链表
int[] arr1 = new int[]{3,1,9,7};
int[] arr2 = new int[]{6,10,16,50};
时间复杂度和空间复杂度
时间复杂度
假设我们有个数组,我们要查找数组中是否存在某个数
如何来衡量写的算法效率更高呢,可以用时间吗?是不可以的。所以我们引入时间复杂度的概念。
每访问一个元素,时间复杂度记为1,如果一个数组的长度为n,如果我们用遍历的方式来进行操作,那么访问n次才能够确定,时间复杂度记为n,其中单层for循环时间复杂度为n,双层for循环时间复杂度为n^2 以此类推
常用的时间复杂度
1(一般Hash结构为1)
n( 一般为遍历)
logn ( 一般为递减的遍历)
n^n *(n次 循环遍历)
空间复杂度
为了衡量我们的算法用了多少额外的空间,我们引入空间复杂度,它是用来形容对空间开辟了多少的一个值,取决于原始数据大小
有序数组和链表的合并
数组的合并
数组的合并的想法很简单,我们首先设置三个指针,两个指针来指向原有数组,一个指针指向合并后的数组,然后比较指针上的值。指针1指向arr1,指针二指向arr2,如果arr1 < arr2就把arr1的数放入新数组然后继续比较下一位。下面我们来看代码实现
public static int[] mergeArr1(int[] arr1,int[] arr2){
if (arr1 == null)return arr2;
if (arr2 == null)return arr1;
int[] result = new int[arr1.length + arr2.length];
int p1 = 0;//用来指向arr1的位置的
int p2 = 0;//用来指向arr2的位置的
int pr = 0;//用来指向结构数组的位置
while(p1 < arr1.length && p2 < arr2.length){
if (arr1[p1] < arr2[p2]){
result[pr] = arr1[p1];
p1 ++;
}else {
result[pr] = arr2[p2];
p2 ++;
}
pr ++;
}
//如果数组的数全取空了
if (p1 == arr1.length){
while (p2 < arr2.length){
result[pr] = arr2[p2];
p2 ++;
}
}else {
while (p1 < arr1.length){
result[pr] = arr1[p1];
p1 ++;
}
}
pr ++;
return result;
}
这个就是主要的流程,但是其中有很多代码冗杂,我们进行一些简化
public static int[] mergeArr2(int[] arr1,int[] arr2){
if (arr1 == null)return arr2;
if (arr2 == null)return arr1;
int[] result = new int[arr1.length + arr2.length];
int p1 = 0;//用来指向arr1的位置的
int p2 = 0;//用来指向arr2的位置的
int pr = 0;//用来指向结构数组的位置
//我们将判断数组是否取空了合并到这里来
while(p1 < arr1.length || p2 < arr2.length){
if (p2 >= arr2.length || p1 < arr1.length && arr1[p1] <= arr2[p2] ){
result[pr] = arr1[p1];
p1 ++;
}else {
result[pr] = arr2[p2];
p2 ++;
}
pr ++;
}
return result;
}
链表的合并
与数组的想法差不多,直接看代码吧
public static Node mergeLinked(Node a,Node b){
if (a == null)return b;
if (b == null)return a;
Node result = null;
if (a.val < b.val){
result = a;
a = a.next;
}else {
result = b;
b = b.next;
}
Node tail = result;
while (a != null || b != null){
if (b == null || a != null && a.val < b.val){
tail.next = a;
tail = a;
a = a.next;
}else {
tail.next = b;
tail = b;
b = b.next;
}
}
return result;
}
排序算法
排序的本质是什么?
排序的本质是比较优先级,按照给的优先及比较然后交换,所有排序主要做的就是两个方法。
比较
public static boolean compare(int a ,int b ){
if (a > b)return true;
return false;
}
交换
public static void change(int[] arr,int aIndex,int bIndex){
int temp = arr[aIndex];
arr[aIndex] = arr[bIndex];
arr[bIndex] = temp;
}
冒泡排序
首先介绍一下我们认识的第一个排序的方法,也是我觉得最简单的排序。冒泡排序的特点是一边比较一边交换,冒泡排序的主要思想是从第一个数到最后一个数逐次比较,如果数组中的数比选中的数要大就交换,如果不是就比较下一个知道所有数全比较完。以下是代码实现,其中的方法为上述方法。
public static void changeSort(int[] arr){
for (int j = 0 ; j < arr.length ; j++){//控制轮次
for (int i = 0 ; i < arr.length-1-i;i++){
if (compare(arr[i],arr[i+1])){
change(arr,i,i+1);
}
}
}
}
虽然代码简单,但是性能不好。
选择排序
选择排序的思想呢和冒泡排序比较接近,选择排序是循环遍历数组找到一个最大值,然后再进行交换。以下是代码实现。
public static void changeSout(int[] arr) {
for (int j = 0; j < arr.length; j++) {
int maxIndex = 0;
for (int i = 0; i < arr.length - j; i++) {
if (compare(arr[i], arr[maxIndex])) {
maxIndex = i;
//循环完一圈找到最大的数
}
}
change(arr, maxIndex, arr.length - 1 - j);
}
}
其实和冒泡排序没啥子太大区别也是双层循环嵌套,所以性能也不是太高。
归并排序
归并排序的思想比较容易理解,一个长的数组之间难以排序,但是只有两个数的数组就是天然有序的了,然后把有序的数组合并起来就排好序了。所以归并排序要做的就是把一个长数组对半分成两个数组,然后再把已经分开的数组再对半分直到数组里边只有两个值,然后把相邻的小数组合并。以下是代码实现。
public static int[] mergeSort(int[] arr){
//基于递归
if (arr == null)return arr;
return mergeSort(arr,0,arr.length);
}
public static int[] mergeSort(int[] arr,int begin,int end){//左闭又开区间
if (end - begin > 2){
//需要向下递归
int[] resultA = mergeSort(arr,begin,begin + (end - begin)/2);
int[] resultB = mergeSort(arr,begin+(end - begin)/2,end);
return mergeArr2(resultA,resultB);
}else if (end - begin == 1){
return Arrays.copyOfRange(arr,begin,end);
}else if (arr[begin] < arr[begin+1]){
return new int[]{arr[begin],arr[begin + 1]};
}else
return new int[]{arr[begin + 1],arr[begin]};
}
快速排序
快速排序也是基于递归实现,要想实现快速排序主要有以下几个步骤,
一 选取组长
二 数组的值与组长比较,小的放左边,大的放右边
三 递归
四 拼接
以下是代码实现。
//简单快速排序 但是在内部创建了很多的ArrarList 比较耗费空间
public static ArrayList<Integer> quickSort(ArrayList<Integer> arr){
if (arr == null || arr.size() <= 1)return arr;
//选组长
int leader = arr.get(0);
//小的放左边,大的放右边
ArrayList<Integer> left = new ArrayList<>();
ArrayList<Integer> right = new ArrayList<>();
for (int i = 1 ; i < arr.size();i++){
if (arr.get(i) <= leader) left.add(arr.get(i));
else right.add(arr.get(i));
}
//递归
ArrayList<Integer> leftResult = quickSort(left);
ArrayList<Integer> rightResult = quickSort(right);
//拼接
ArrayList<Integer> result = new ArrayList<>();
result.addAll(leftResult);
result.add(leader);
result.addAll(rightResult);
return result;
}
上边的快速排序是只有基础的思想,但是性能不好,我们对他进行一些改进,以下是代码实现
public static void quickSort(int[] arr){
quickSort(arr,0,arr.length-1);
}
public static void quickSort(int[] arr ,int low , int high){
if (low >= high) return;
int leader = arr[low];
while(low < high){
while (low < high && arr[high] >= leader) high--;
arr[low] = arr[high] ;
while (low < high && arr[low] <= leader) low++;
arr[high] = arr[low] ;
}
arr[low] = leader;
quickSort(arr,low,high-1);
quickSort(arr,low+1,high);
}