在数据结构课里,一般会将查找和排序放在一起,大部分人都会感觉查找比排序容易。但是我们研究过算法之后就会发现查找远远难于排序,因为常见的排序方法是相对固定的。而查找除了最基本的二分查找外,还包含非常广的内容,二叉树,各种树,Hash,大数据下的查找,而动态规划和回溯等本身也是在寻找某个特定的目标。所以查找散步在整个算法中,我们不单独列了。
排序我们先看看都有哪些常见的算法。还有一些基于某些特征的典型算法题,之后我们就一边整理排序算法,一边见识一下这些变形题。
常见的排序方法有:1冒泡排序。2.选择排序。3 插入排序。4.快速排序。5.归并排序。6.快速排序。7.归并排序。8.堆排序。9.优先级队列。10.桶排序。11.基数排序。12.希尔排序。13位图排序.
本文我们先看几个基本的算法。
1.调用库函数Arrays.sort
这个在比较复杂的算法题中可以直接用,如果就是为了考察排序,那自然不行。
import java.util.Arrays;
public class Solution {
public int[] MySort (int[] arr) {
//调用库函数sort;
Arrays.sort(arr);
return arr;
}
}
而我们后面在桶排序中,这是一个比较复杂的算法,期间还需要用到排序算法,此时就顺理成章使用库方法了。
2.冒泡排序BubbleSort
最基本的排序算法,这个题目就是热身或者在某些场景下使用其变型方法,比如前面链表里奇偶调整的例子中就用到了。在考察排序的题里是不能用的,因为会出现运行超时的情况。
public int[] MySort (int[] arr) {
if(arr.length<2){
return arr;
}
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
swap(arr,j,j+1);
}
}
}
return arr;
}
public void swap(int[]arr,int i, int j){
int tmp;
tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
3.选择排序
选择排序和冒泡排序有一点点像,选择排序是默认前面都是已经排序好的,然后从后面 选择最小的放在前面排序好的的后面,首先第一轮循环的时候默认的排序好的为空,然 后从后面选择最小的放到数组的第一个位置,第二轮循环的时候默认第一个元素是已经 排序好的,然后从剩下的找出最小的放到数组的第二个位置,第三轮循环的时候默认前 两个都是已经排序好的,然后再从剩下的选择一个最小的放到数组的第三个位置,以此 类推。下面看一下代码。
public static void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int index = i;
for (int j = i + 1; j < array.length; j++) {
if (array[index] > array[j]) {
index = j;
}
}
if (i != index) {
swap(array, i, index);
}
}
}
public static void swap(int[] A, int i, int j) {
A[i] ^= A[j];
A[j] ^= A[i];
A[i] ^= A[j];
}
我们看到每轮循环的时候并没有直接交换,而是从他后面的序列中找到最小的记录一下 他的index索引,最后再交换.
这里的交换方法是不是和平时见的不一样,这种位操作的方式效率更高,如果理解并在面试时写出来,也是个不错的加分项,常规是定义一个临时变量,然后交换。其实这里还有一个骚写法:
public static void swap2(int[] A, int i, int j) {
A[i] = A[i] + A[j];
A[j] = A[i] - A[j];
A[i] = A[i] + A[j];
}
上面理解已经是合格了,为了加深理解,下面看一下他的递归是怎么实现的。
public static void selectSortRe(int[] array, int index) {
if (index == array.length)
return;
int min = index, i = index + 1;
for (; i < array.length; i++) {
if (array[i] < array[min]) min = i;
}
if (min != index) {
swap(array, index, min);
}
selectSortRe(array, ++index);
}
5. 插入排序
插入排序的原理是默认前面的元素都是已经排序好的,然后从后面逐个读取插入到前面排序好的合适的位置,就相当于打扑克的时候每获取一张牌的时候就插入到合适的位置一样。插入排序可以分为两种,一种是直接插入还一种是二分法插入,直接插入的原理比较简单,就是往前逐个查找直到找到合适的位置然后插入,二分法插入是先折半查找,找到合适的位置然后再插入。说到二分法查找,等排序完之后就会介绍查找,有多种包括斐波那契查找,哈希查找,二分法查找等多个,其实这里面也可以使用,我们先看一下简单的直接插入排序代码:
public int[] MySort (int[] arr) {
if(arr==null || arr.length<2){
return null;
}
for(int i=1;i<arr.length;i++){
int j=i;
int temp=arr[i];
for(;j>0;j--){
if(arr[j-1]>temp){
arr[j]=arr[j-1];
}else{
break;
}
}
arr[j]=temp;
}
return arr;
}
这里还可以在查找的时候使用二分查找,数据量大的时候,效率肯定更高,但是编写的难度也高了:
public int[] MySort (int[] arr) {
if(arr==null || arr.length<2){
return null;
}
for(int i=1;i<arr.length;i++){
if(arr[i-1]>arr[i]){
int key=arr[i];
int low=0;
int high=i-1;
while(low<=high){
int mid=(low+high)>>1;
if(arr[mid]>key){
high=mid-1;
}else{
low=mid+1;
}
}
for(int j=i;j>low;j--){
arr[j]=arr[j-1];
}
arr[low]=key;
}
}
return arr;
}