1、快速排序
原理:该算法是分治法思想的一个应用,在数组中选这一个元素作为基准值,小于基准指的放到数组左边,大于基准值的放到数组右边,等于的随意放,然后在该基准值的左边区域在选择一个基准值,重复挑选步骤,在该基准值的右边也选择一个基准值,重复挑选步骤。最后子序列为长度为1的时候结束排序。
稳定性:不稳定
复杂度:时间复杂度O(nlogn),空间复杂度O(nlogn)
算法描述:
假设有无序数组:[6,5,4,6,9,3,7,6,10,6,8]
第一轮循环:选择下标为0的值作为基准值,即arr[0]=6,小于等于6的放左边,大于6的放右边,循环后可得出数组[[5,4,6,3,6,6,6][9,7,10,8]]
第二次循环:分别对左右数组挑选基准值并拆分,左边基准值为arr[0]=5,右边基准值arr[7]=9,分别对左边和右边的子数组序列进行挑选,挑选后的结果为[[[4,3,5][6,6,6,6]][[7,8,9][10]]]
…
最后循环的结果即为排序后的结果。
代码实现:(代码千千万,这里只是其中一种)
/**
* @author hdj
* @param arr 待排序数组
* @return
* @desc 快速排序
*/
public static void quickSort(int[] arr){
if(arr==null||arr.length<2){
return ;
}
long ctime=System.currentTimeMillis();
quickSplit(0,arr.length-1,arr);
System.out.println("耗时="+(System.currentTimeMillis()-ctime)+"ms");
}
private static void quickSplit(int start,int end,int[] arr){
if(start>=end){
return ;
}
int baseVal=arr[start];
int idx=start+1;
for(int j=idx;j<=end;j++){
if(arr[j]<baseVal){
int tmp=arr[j];
arr[j] = arr[idx];
arr[idx] = tmp;
idx++;
}
}
int tm=arr[start];
arr[start] = arr[idx-1];
arr[idx-1] = tm;
quickSplit(start,idx-2,arr);
quickSplit(idx,end,arr);
}
2、堆排序
堆的性质:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
假设父节点下标为i,则该节点下左右子节点的下标分别为2i+1,2i+2.
原理:通过将给定数组构建成大根堆或者小根堆来进行排序,比如构建大根堆,保证根节点第一排序后是堆中最大的数,然后将根节点与最右侧的子节点交换位置,减掉最后一个左右节点,接着对剩余节点递归构建大根堆,重复构建和交换步骤,直到堆中剩余一个元素为止。
稳定性:不稳定
复杂度:时间复杂度O(nlongn),空间复杂度O(1)
算法描述:
假设有无序数组:[5,3,6,4,7,8,2]
第一次构建大根堆后的数组为[8,7,5,4,3,6,2],将根节点与堆中最后一个节点交换得[2,7,5,4,3,6,8],重新构建大根堆(此时最后一个节点已经被剪掉)。
第二次构建大根堆后的数组为[7,2,6,4,3,5,8],将根节点与堆中最后一个节点交换得[5,6,2,4,3,7,8],重新构建大根堆(此时倒数第二个节点已经被剪掉)。
…
最后当堆中只剩一个元素的时候即是排序好的时候,排序结果如下[2,3,4,5,6,7,8]
代码实现:(代码千千万,这里只是其中一种)
/**
* @author hdj
* @param arr 待排序数组
* @return
* @desc 堆排序算法
*/
private static void heapSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
buildMaxHeap(arr.length,arr);
for(int i=arr.length-1;i>=0;i--){
swap(arr,0,i);
heapSwitch(i-1,i,arr);
}
}
/**
* @author hdj
* @param end 堆长度
* @param arr 待排序数组
* @return
* @desc 第一次构建大根堆
*/
private static void buildMaxHeap(int end,int[] arr){
heapSwitch(end,end,arr);
}
/**
* @author hdj
* @param i 当前父节点
* @param maxLen 当前剩余的最大堆长度
* @param arr 待排序数组
* @return
* @desc 构建大根堆
*/
private static void heapSwitch(int i,int maxLen,int[] arr){
if(i<0){
return;
}
int left=2*i+1,right=2*i+2,large=i;
if(left<maxLen&&arr[large]<arr[left]){
large=left;
}
if(right<maxLen&&arr[large]<arr[right]){
large=right;
}
if(large!=i) {
int tmp = arr[i];
arr[i] = arr[large];
arr[large] = tmp;
}
heapSwitch(i-1,maxLen,arr);
}
3、二分查找
原理:在给定的有序数组中,通过查找值与子数组序列的中间值比较来不断缩小查找范围,最终得出结果,找到就返回该值的索引,找不到返回-1.
复杂度:时间复杂度为O(log2n)
算法描述:
假设有序数组:[2,3,4,5,6,7,8,9],要查找的值为8。
第一次比较:mid=arr.length/2=4。Arr[mid]=6<8,则从右边区间缩小范围进行查找,此时需要查找的子数组序列为[7,8,9].
第二次比较:mid=(mid+end)/2=6,arr[6]=8==8,发现值匹配,返回下标6,查找结束。
因为每次都是折半查找,假设有n个元素,当前查找次数为i,则时间复杂度 t ( n ) = i + t ( n / 2 ( i − 1 ) ) t(n)=i+t(n/2^{(i-1)}) t(n)=i+t(n/2(i−1)),当 2 ( i − 1 ) 2^{(i-1)} 2(i−1)趋近于n的时候, t ( n ) = i + t ( 1 ) = i t(n)=i+t(1)=i t(n)=i+t(1)=i,因为 2 ( i − 1 ) = n 2^(i-1)=n 2(i−1)=n,可得 i = l o g 2 n + 1 i=log_2n+1 i=log2n+1,所以 t ( n ) = l o g 2 n + 1 t(n)=log_2n+1 t(n)=log2n+1,即 t ( n ) = O ( l o g 2 n ) t(n)=O(log_2n) t(n)=O(log2n)
算法实现:(算法千千万,这里只是其中一种)
/**
* @param sortedArr 有序数组
* @param target 查找目标值
* @return
* @author hdj
* @desc 二分查找算法
*/
private static int binarySearch(int[] sortedArr, int target) {
if (sortedArr == null || sortedArr.length == 0) {
return -1;
}
int mid = (sortedArr.length - 1) / 2;
int left = 0, right = sortedArr.length - 1;
while (left <= right) {
if (sortedArr[mid] == target) {
return mid;
}
if (sortedArr[mid] > target) {
right = mid - 1;
mid = (mid - 1 + left) / 2;
}
if (sortedArr[mid] < target) {
left = mid + 1;
mid = (mid + 1 + right) / 2;
}
}
return -1;
}
4、线性查找算法-BFPRT算法
BFPRT算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分析,BFPRT可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂度,五位算法作者做了精妙的处理。
算法描述:
- 将n个元素每5个一组,分成n/5(上界)组。
- 取出每一组的中位数,任意排序方法,比如插入排序。
- 递归的调用selection算法查找上一步中所有中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。
- 用x来分割数组,设小于等于x的个数为k,大于x的个数即为n-k。
- 若i==k,返回x;若i<k,在小于x的元素中递归查找第i小的元素;若i>k,在大于x的元素中递归查找第i-k小的元素。 终止条件:n=1时,返回的即是i小元素。 复杂度:BFPRT算法能够做到时间复杂度是 O(N)
算法实现:(算法千千万,这里只是其中一种)
/**
* @param start 查找的开始位置
* @param end 查找的结束位置
* @param arr 查找的数组
* @param k 第k个值
* @return
* @author hdj
* @desc 线性查找算法
*/
private static int bfprtSearch(int start, int end, int[] arr, int k) {
if (k < 0 || arr.length == 1 || k > arr.length) {
return arr[0];
}
int p = getPivot(start, end, arr);
int idx = partition(start, end, arr, p);
if (idx == k) {
return arr[k];
} else if (k > idx) {
return bfprtSearch(idx + 1, end, arr, k);
} else {
return bfprtSearch(start, idx - 1, arr, k);
}
}
/**
* @author hdj
* @param start 区间开始位置
* @param end 区间结束位置
* @param arr 待排序数组
* @return
* @desc 获取区间内的基准值
*/
private static int getPivot(int start, int end, int[] arr) {
if (start == end) {
return arr[start];
}
int seg = (end - start) / 5;
int[] midArr = new int[seg + 1];
int cnt = 0, begin = -1;
for (int i = 0; i <= seg; i++) {
begin = start + i * 5;
if (i < seg) {
quickSplitForBfprt(begin, begin + 4, arr, begin+2);
midArr[cnt++] = arr[begin + 2];
} else {
quickSplitForBfprt(begin, end, arr, begin+2);
int ca = (begin + end) / 2;
midArr[cnt++] = arr[ca];
}
}
quickSplitForBfprt(0, midArr.length - 1, midArr, midArr.length / 2);
return midArr[midArr.length / 2];
}
/**
* @author hdj
* @param start 子序列开始位置
* @param end 子序列结束位置
* @param arr 待分区排序数组
* @param pivot 基准值,大于该值放右边,小于该值放左边
* @return
* @desc 对数组进行分区排序,快速排序的一次遍历
*/
private static int partition(int start, int end, int[] arr, int pivot) {
if (start == end) {
return start;
}
int mid = start, pIdx = -1;
//这里对整个区间进行遍历,小于等于pivot的都交换到左边,并记录最后一次和pivot值相等的交换后的下标位置
for (int j = start; j <= end; j++) {
if (arr[j] == pivot) {
pIdx = mid;
}
if (arr[j] <= pivot) {
swap(arr, mid, j);
mid++;
}
}
//根据上一步for循环记录的下标,将pivot交换到当前指针-1位置,保证pivot左边全都是小于等于pivot的,右边全都是大于pivot的
if (pIdx != -1) {
swap(arr, pIdx, mid - 1);
}
return mid - 1;
}
/**
* @author hdj
* @param start 排序开始位置
* @param end 排序结束位置
* @param arr 待排序数组
* @param mid 排序中间位置
* @return
* @desc 配合线性查找bfprt算法改进的快速排序算法,因为bfprt算法排序的目的只是为了获取中位数,所以从大于中位数下标的起始值的排序都不需要
*/
private static void quickSplitForBfprt(int start, int end, int[] arr, int mid) {
if (start >= end || start > mid) {
return;
}
int baseVal = arr[start];
int idx = start + 1;
for (int j = idx; j <= end; j++) {
if (arr[j] < baseVal) {
int tmp = arr[j];
arr[j] = arr[idx];
arr[idx] = tmp;
idx++;
}
}
int tm = arr[start];
arr[start] = arr[idx - 1];
arr[idx - 1] = tm;
quickSplitForBfprt(start, idx - 2, arr,(start+idx-2)/2);
quickSplitForBfprt(idx, end, arr,(idx+end)/2);
}
5、广度优先搜索算法(BFS)
原理:属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形: 1、把根节点放到队列的末尾。
2、每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。 4、如果遍历整个树还没有找到,结束程序。
最短路径查找
算法实现:(算法千千万,这里只是其中一种)
import java.util.LinkedList;
import java.util.Queue;
public class BFSSearchMain {
//矩阵长度
private static int rectLength=10;
//记录当前元素是否已经访问过
private static int[][] visited=new int[rectLength][rectLength];
//下一步可能移动的方向
private static int[][] nextDirect=new int[][]{{-1,0},{0,1},{0,-1},{1,0}};
public static void main(String[] args){
/*
* 在一个n*n的矩阵里走,从原点(0,0)开始走到终点(n-1,n-1),
* 只能上下左右4个方向走,只能在给定的矩阵里走,求最短步数
* 。n*n是01矩阵,0代表该格子没有障碍,为1表示有障碍物。
* */
int[][] rect=new int[][]{
{0,1,0,0,0,0,0,1,0,1},
{0,1,1,0,1,0,0,0,0,1},
{0,1,0,0,1,1,1,1,0,1},
{0,0,0,1,1,1,0,1,0,1},
{1,0,1,1,0,1,0,1,0,1},
{1,0,0,1,0,1,0,1,0,1},
{1,1,0,0,0,0,0,1,0,1},
{0,0,0,1,1,1,0,0,0,1},
{0,1,0,1,0,1,0,1,0,1},
{0,1,0,1,0,0,0,1,0,0},
};
System.out.println("最短步骤为:"+bfsSearch(rect));
}
/**
* @author hdj
* @param rect 给定的矩阵
* @return
* @desc 广度优先搜索算法
*/
private static int bfsSearch(int[][] rect){
Node head=new Node(0,0,0);
Queue<Node> queue= new LinkedList<>();
queue.add(head);
while(!queue.isEmpty()){
Node n=queue.poll();
visited[n.x][n.y]=1;
for(int i=0;i<4;i++){
int x=n.x+nextDirect[i][0];
int y=n.y+nextDirect[i][1];
if(x==rectLength-1&&y==rectLength-1){
return n.step+1;
}
if(x>=0&&y>=0&&x<rectLength&&y<rectLength&&visited[x][y]!=1&&rect[x][y]==0){
Node nextNode=new Node(x,y,n.step+1);
queue.add(nextNode);
}
}
}
return -1;
}
static class Node{
private int x;
private int y;
private int step;
public Node(int x,int y,int step){
this.x=x;
this.y=y;
this.step=step;
}
}
}