几种常用排序算法简单实现和分析

  • 写在之前


  • 代码实现 Java
  • 设计模式 策略模式
// 接口定义
public interface Sorter {
    public void sort(int []data);
}
// 测试入口函数
public static void main(String[] args) {
        int []data = new int[] {2,5,6,3,4,6,1,8,9,12,56,4,3,7,9,6,8};
        Sorter sorter = new //具体实现类
        sorter.sort(data);
        for(int i:data) {
            System.out.println(i);
        }
    }

  • 冒泡排序

似乎是这个世界上最简单的排序算法,很多Coder最初学会的排序算法, 简单思路清晰,通过一次次的循环找出最值元素将其浮到“边界”,因其过程十分相似冒泡,就得到了这个十分形象的名字。

public class BubbleSort implements Sorter{

    @Override
    public void sort(int[] data) {
        for(int i=0; i data[j]) {
                   //交换 data[i]和data[j]的值  当时突然想起了怎么在不引入中间变量交换值 0.0
                    data[i] = data[i]^data[j];
                    data[j] = data[i]^data[j];
                    data[i] = data[i]^data[j];
                }
            }
        }
    }
}
  • 时间复杂度 O(n^2)
  • 空间复杂度 O(1) (雾 如果这样写明明是0
  • 稳定的排序

  • 直接插入排序

一种简单暴力排序算法,将待排序的数字依次插入一个有序数组中,平时很少用到,反而在一些往已经有序的数组插入数据的场合使用。

public class InsertionSort implements Sorter{

    @Override
    public void sort(int[] data) {
        for(int i=0; i=0;j--) {
                if(data[j] > data[j+1]) {
                    int temp = data[j+1];
                    data[j+1] = data[j];
                    data[j] = temp;
                }else {
                    break;
                }
            }   
        }
    }
}
  • 时间复杂度 O(n^2)
  • 空间复杂的有 O(1)
  • 稳定 的排序
    相比于冒泡排序没什么优点 还多了一句 怪不得很少人使用。

  • 归并排序

一种思想简单,且具有魔力的排序,使用分治的思想 先拆分数据为2部分 分别保证左边和右边同样有序 再将其有序地归并起来,其实简而言之,我觉得归并排序本身就是在不停地拆和并。使用的情景非常丰富,是一种高效的排序手段。

  • 简单举例

8,13,9,12,56,4,3,7,12;

拆成子元素
8        9       56       3      12
    13       12       4      7
第一趟归并
8 13         4 56        12
      9 12         3 7
第二趟
8 9 12 13    12
        3 4 7 56 
//中间略归并一步
3 4 7 8 9 12 12 13 56
public class MergcSort implements Sorter{

    @Override
    public void sort(int[] data) {
        int[] temp = new int[data.length]
        mergcSort(0, data.length-1, data,temp);     
    }
    
    private void mergcSort(int left,int right,int[]data,int []temp) {
        if(left == right) {
         //当拆分到只有一个元素的时候当然有序咯
            return;
        }
        // 拆拆拆
        int mid = (left+right)/2;
        // 递归拆左边
        mergcSort(left, mid, data);
        // 递归拆右边
        mergcSort(mid+1, right, data);
        
        int x = left,y = mid +1,loc =left;
        while(x<=mid || y<=right) {
            if(x <= mid &&(y>right||data[x] < data[y])) {
             //分2种情形
            //1. x <= mid && y <= right 这时候data[x] 的值小于data[y] 按照原则 谁小谁上
            //2. x <= mid && y > right 即右边的已经全部填充到数组里了 这时候只好填左边的了
                temp[loc] = data[x];
                x++;
            }else {
                temp[loc] = data[y];
                y++;
            }
            loc++;
        }
        // 毫无技巧地将中间数组的值填充回来
        for(int i=left;i<=right;i++) {
            data[i] = temp[i];
        }
        
    }
  • 时间复杂度O(n*log(n))
  • 空间复杂度 O(n)
  • 稳定的排序

  • 快速排序

强大的排序,是最常用的排序算法之一(目测要是是稳定排序的话使用会更丰富),快速排序其实非常简单 简单来说就是不断挖坑和填坑的过程,特点排序效率高,空间复杂度低,非常难得。

  • 简单举例

8,13,9,12,56,4,3,7,12;

选取哨兵 简单取第一位挖掉 【】表示新填充   ()代表挖掉了 哨兵 = 8;
(8) 13 9 12 56 4 3 7 12 ;            挖掉哨兵
#1【7】 13 9 12 56 4 3 (7) 12         从右搜索第一个小于8的 挖掉并填充到上次挖的地方 7
#2 7 (13) 9 12 56 4 3 【13】 12       从左搜索第一个大于8的 挖掉填充到上次挖的地方 13
7 【3】9 12 56 4 (3)13 12             重复#1
7 3 (9)12 56 4 【9】 13 12            #2
7 3 【4】 12  56 (4) 9 13 12          。。。
7  3  4  (12) 56 【12】 9 13 12 
 最后左右重逢了 填充哨兵到相逢的地方
7 3 4 8 56 12 9 13 12 
递归处理左右
[ 7 3 4 ] 8 [56 12 9 13 12]
......
  • 代码实现
public class QuickSort implements Sorter{

    @Override
    public void sort(int[] data) {      
        quickSort(0, data.length-1, data);
    }
    
    private void quickSort(int left,int right,int []data) {
        if(right <= left) {
            return;
        }
        int x = left,y = right;
        int temp = data[x];
        while(x < y) {
            while(y > x && data[y] > temp) {
                y--;
            }
            if(y > x) {
                data[x++]=data[y];
            }
            while(x < y && data[x] < data[y]) {
                x++;
            }
            if(x < y) {
                data[y--] = data[x];
            }
        }
        data[x] = temp;
        quickSort(left, x-1, data);
        quickSort(x+1, right, data);
    }
}

听起来好像很复杂 实际实现起来非常简单 其中2个if是为了保证左右指针不重逢 毕竟重逢意味着填充哨兵,本次排序的结束和分治的开始

  • 时间复杂度O(n*log(n))
  • 空间复杂度 O(1)
  • 不稳定的排序

  • 堆排序

STL中优先队列的底层实现,虽然在一次排序上表现不够优秀,但是非常适合那种不停地插入和取出最值的情景,堆排序事实上是最大堆或者最小堆,属于树形结构这里我就简单写一个堆,接下来不适合0基础的人查看。

public class Heap {

    private int[] data;
    private int size;

    public Heap(int size) {
        data = new int[size];
        size = 0;
    }
       //得到堆顶元素
    public int getTop() {
        return data[0];
    }
 // 入堆
 // 将入堆元素放到堆末尾 再从低而上做一次维护

    public void push(int input) {
        int current = size;
        data[current] = input;
// 计算 父节点的位置
        int father = (current - 1) / 2;
// 维护 这里保证堆中所有的儿子都比父亲大 否则就交换位置 继续向上维护
        while (data[current] < data[father]) {
            data[current] = data[current] ^ data[father];
            data[father] = data[current] ^ data[father];
            data[current] = data[current] ^ data[father];

            current = father;
            father = (father - 1) / 2;
        }
        size++;
    }
// 最值元素出堆 首先将要出堆的元素交换到末尾 再顶定而下做一次维护
    public int pop() {
        int temp = data[0];
        data[0] = data[size-1];
        data[size-1] = temp;    
        size--;     
        update(0,size);
        return data[size];
    }
//自定而下维护函数 如果发现子节点大于父节点的值交换位置 再从新节点位置继续维护  专业的叫法叫 沉底
    private void update(int pos,int size) {
        int lchild = pos*2+1;
        int rchild = pos*2+2;
        
        int min_pos = pos;
        if(lchild < size && data[lchild] < data[min_pos]) {
                min_pos = lchild;
            }
        if(rchild < size && data[rchild] 
  • 时间复杂度O(n*log(n))
  • 空间复杂度 O(1)
  • 不稳定的排序

你可能感兴趣的:(几种常用排序算法简单实现和分析)