10种排序算法

目录

冒泡排序

选择排序

插入排序

希尔排序

归并排序

快速排序

堆排序

计数排序

桶排序

基数排序

数据结构:数组、链表、队列、栈、前(中、后)缀表达式、哈希表、二叉树、图

算法:递归、回溯、排序、分治、动态规划、暴力匹配、KMP、贪心、 普利姆-Prim(解决修路)、克鲁斯卡尔-Kruskal(解决公交)、 迪杰斯特拉-Dijkstra(解决最短路径)、弗洛伊德-Floyd(解决最短路径) 、骑士周游(用到回溯、贪心);https://www.bilibili.com/video/BV1E4411H73v?p=86

练习5种:冒泡排序、选择排序、插入排序、希尔排序、计数排序

比较排序:快速排序、归并排序、冒泡排序、堆排序 (每个数必须和其他数比较,才能确定自己的位置)  ;在快速排序、归并排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn) ;冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n2)

优势:比较排序适用于各种规模的数据,也不在乎数据的分布,适用于一切需要排序的情况

非比较排序:计数排序、基数排序、桶排序 (通过确定每个元素之前,应该有多少个元素来排序,则唯一确定了arr[i]在排序后数组中的位置);非比较排序只要确定每个元素之前已有的元素个数,所以一次遍历即可解决,算法时间复杂度O(n)

特点:时间复杂度低,但由于需要占用空间来确定唯一位置,所以对数据规模和数据分布有一定的要求

冒泡排序

1、冒泡排序:从第一个开始,依次两两比较,大的放在后面(升序)
最佳情况:T(n) = O(n)、最坏:T(n) = O(n^2)、平均:T(n) = O(n^2)

// 1、冒泡排序:从第一个开始,依次两两比较,大的放在后面(升序)
  // 最佳情况:T(n) = O(n)、最坏:T(n) = O(n^2)、平均:T(n) = O(n^2)
  public static int[] bubbleSort(int[] array) {
    int k = array.length - 1;   // k用来记录每趟排序的最大的交换位置
    int pos = 0;   // pos记录最后一次交换的位置
    for (int i = 0; i < array.length - 1; i++) {
      int flag = 0;   // 每一趟前都将flag标志先置为0
      for (int j = 0; j < k; j++) {
        if (array[j] > array[j + 1]) {
          int temp = array[j];
          array[j] = array[j + 1];
          array[j + 1] = temp;
          flag = 1;   // 元素发生了交换,flag置为1
          pos = j;   // pos存放循环里最后一次交换的位置j
        }
      }
      k = pos;  // 下一内层循环仅循环到0到这次得到的k之间
// 如果一趟下来flag没有变化,即元素本来就是有序,就直接return
      if (flag == 0) {
        return array;
      }
    }
    return array;
  }

选择排序

2、选择排序:每次找到最小数,记录下序号,与第i趟对换
表现最稳定的排序算法之一 ,无论什么数据进去都是O(n^2)的时间复杂度 

public static int[] selectionSort(int[] array) {
    int minIndex, j, temp = 0;
    for (int i = 0; i < array.length - 1; i++) {
      minIndex = i;
      for (j = i + 1; j < array.length; j++) {
        if (array[minIndex] > array[j]) {
          minIndex = j;  // 比较n-i个数,找出最小值的序号
        }
      }
      temp = array[minIndex];
      array[minIndex] = array[i];
      array[i] = temp;
    }
    return array;
}

插入排序

3、插入排序:从未排序序列取出数,和已排序序列从后往前比较,小于,已排序序列就空出一个位置,由该数插入
最佳情况:T(n) = O(n)、最坏情况:T(n) = O(n^2)、平均情况:T(n) = O(n^2)

public static int[] insertSort(int[] array) {
    int sortNum = 0;    // 需要比较的数
    int preIndex = 0;   // 已排序序列最后一个数
    for (int i = 0; i < array.length - 1; i++) {
      preIndex = i;
      sortNum = array[i + 1];
      // 需比较的数 一直和 已排序的序列比较
      while (preIndex >= 0 && sortNum < array[preIndex]) { 
        array[preIndex + 1] = array[preIndex];
        preIndex--;
      }
      array[preIndex + 1] = sortNum;  // 把需要比较的数 放到 已排序序列
    }
    return array;
}

希尔排序

4、希尔排序:升级后的插入排序,将数组 按希尔增量序列(n/2,n/4,...) 依次分组,对每组单独做插入排序,直到 gap为1

最佳情况:T(n) = O(nlog2 n)、最坏情况:T(n) = O(nlog2 n)、平均情况:T(n) =O(nlog2n)

public static int[] shellSort(int[] array) {
    int temp, gap = array.length / 2;
    while (gap > 0) {  // 分组后
      for (int i = gap; i < array.length; i++) {  // gap后的数为 需比较的数
        int preIndex = i - gap;
        temp = array[i];
        while (preIndex >= 0 && array[preIndex] > temp) {
          array[preIndex + gap] = array[preIndex];
          preIndex -= gap;
        }
        array[preIndex + gap] = temp; //放置 需比较的数
      }
      gap /= 2;
    }
    return array;
}

归并排序

5、归并排序:建立在归并操作的一种排序算法,采用分治法(Divide and Conquer)的一个典型应用
先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并;始终是O(n log n)的时间复杂度,代价是需要额外的内存空间

public static int[] mergeSort(int[] array) {
    if (array.length < 2) return array;
    int mid = array.length / 2;
    int[] left = Arrays.copyOfRange(array, 0, mid);
    int[] right = Arrays.copyOfRange(array, mid, array.length);
    return merge(mergeSort(left), mergeSort(right));
  }

  public static int[] merge(int[] left, int[] right) {
    int[] result = new int[left.length + right.length];
    for (int index = 0, i = 0, j = 0; index < result.length; index++) {
      if (i >= left.length)
        result[index] = right[j++];
      else if (j >= right.length)
        result[index] = left[i++];
      else if (left[i] > right[j])
        result[index] = right[j++];
      else
        result[index] = left[i++];
    }
    return result;
  }

快速排序

6、快速排序:从数组中找一个数作基准值,小于基准值的放前面,大于的放后面,等于的放任一边,递归的找基准值
最佳情况:T(n) = O(nlogn)、最差情况:T(n) = O(n2)、平均情况:T(n) = O(nlogn)

public static void quickSort(int[] array, int low, int high) {
    if (low < high) {
      // 找寻基准数据的正确索引
      int index = getIndex(array, low, high);
      // 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
      quickSort(array, low, index - 1);
      quickSort(array, index + 1, high);
    }
}

public static int getIndex(int[] arr, int low, int high) {
    // 基准数据
    int tmp = arr[low];
    while (low < high) {
        // 当队尾的元素大于等于基准数据时,向前挪动high指针
        while (low < high && arr[high] >= tmp) {
            high--;
        }
        // 如果队尾元素小于tmp了,需要将其赋值给low
        arr[low] = arr[high];
        // 当队首元素小于等于tmp时,向前挪动low指针
        while (low < high && arr[low] <= tmp) {
            low++;
        }
        // 当队首元素大于tmp时,需要将其赋值给high
        arr[high] = arr[low];
    }
    // 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
    // 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
    arr[low] = tmp;
    return low; // 返回tmp的正确位置
}

堆排序

 7、堆排序算法:利用堆数据结构设计,满足堆积的性质,即子结点的键值或索引总是小于(或者大于)它的父节点
 最佳情况:T(n) = O(nlogn)、最差情况:T(n) = O(nlogn)、平均情况:T(n) = O(nlogn)

static int len; //声明全局变量,用于记录数组array的长度;

public static int[] heapSort(int[] array) {
    len = array.length;
    if (len < 1) return array;
    //1.构建一个最大堆
    buildMaxHeap(array);
    //2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆
    while (len > 0) {
      swap(array, 0, len - 1);
      len--;
      adjustHeap(array, 0);
    }
    return array;
}


// 建立最大堆
public static void buildMaxHeap(int[] array) {
    //从最后一个非叶子节点开始向上构造最大堆
    //for循环这样写会更好一点:i的左子树和右子树分别2i+1和2(i+1)
    for (int i = (len / 2 - 1); i >= 0; i--) {
      adjustHeap(array, i);
    }
}

public static void swap(int[] array, int i, int j) {
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

// 调整使之成为最大堆
public static void adjustHeap(int[] array, int i) {
    int maxIndex = i;
    //如果有左子树,且左子树大于父节点,则将最大指针指向左子树
    if (i * 2 < len && array[i * 2] > array[maxIndex])
      maxIndex = i * 2; 
    //如果有右子树,且右子树大于父节点,则将最大指针指向右子树
    if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex])
      maxIndex = i * 2 + 1;
    //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
    if (maxIndex != i) {
      swap(array, maxIndex, i);
      adjustHeap(array, maxIndex);
    }
}

计数排序

8、计数排序:使用一个额外的数组C,其中第i个元素是待排序数组A中值为i的元素个数,然后根据数组C来将A中的元素排到正确的位置,它只能对整数进行排序
最佳情况:T(n) = O(n+k)、最差情况:T(n) = O(n+k)、平均情况:T(n) = O(n+k)

public static int[] countingSort(int[] array) {
    if (array.length == 0) {
      return array;
    }
    int bias, min = array[0], max = array[0];
    for (int i = 1; i < array.length; i++) {
      if (array[i] > max) {
        max = array[i];
      }
      if (array[i] < min) {
        min = array[i];
      }
    }
    bias = 0 - min;
    int[] bucket = new int[max - min + 1];
    Arrays.fill(bucket, 0);
    for (int i = 0; i < array.length; i++) {
      bucket[array[i] + bias]++;
    }
    int index = 0, i = 0;
    while (index < array.length) {
      if (bucket[i] != 0) {
        array[index] = i - bias;
        bucket[i]--;
        index++;
      } else {
        i++;
      }
    }
    return array;
}

桶排序

9、桶排序:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序)
设置一个BucketSize,作为每个桶能放置多少个不同数值(例如BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3)
最佳情况:T(n) = O(n+k)、最差情况:T(n) = O(n+k)、平均情况:T(n) = O(n2)

public static ArrayList<Integer> bucketSort(ArrayList<Integer> array, int bucketSize) {
    if (array == null || array.size() < 2)
      return array;
    int max = array.get(0), min = array.get(0);
    // 找到最大值最小值
    for (int i = 0; i < array.size(); i++) {
      if (array.get(i) > max)
        max = array.get(i);
      if (array.get(i) < min)
        min = array.get(i);
    }
    int bucketCount = (max - min) / bucketSize + 1;
    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);
    ArrayList<Integer> resultArr = new ArrayList<>();
    for (int i = 0; i < bucketCount; i++) {
      bucketArr.add(new ArrayList<Integer>());
    }
    for (int i = 0; i < array.size(); i++) {
      bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
    }
    for (int i = 0; i < bucketCount; i++) {
      if (bucketSize == 1) { // 如果带排序数组中有重复数字时
        for (int j = 0; j < bucketArr.get(i).size(); j++)
          resultArr.add(bucketArr.get(i).get(j));
      } else {
        if (bucketCount == 1)
          bucketSize--;
        ArrayList<Integer> temp = bucketSort(bucketArr.get(i), bucketSize);
        for (int j = 0; j < temp.size(); j++)
          resultArr.add(temp.get(j));
      }
    }
    return resultArr;
}

基数排序

10、基数排序:基数排序也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),k为数组中的数的最大的位数;

public static int[] radixSort(int[] array) {
    if (array == null || array.length < 2)
      return array;
    // 1.先算出最大数的位数;
    int max = array[0];
    for (int i = 1; i < array.length; i++) {
      max = Math.max(max, array[i]);
    }
    int maxDigit = 0;
    while (max != 0) {
      max /= 10;
      maxDigit++;
    }
    int mod = 10, div = 1;
    ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
    for (int i = 0; i < 10; i++) {
      bucketList.add(new ArrayList<Integer>());
    }
    for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
      for (int j = 0; j < array.length; j++) {
        int num = (array[j] % mod) / div;
        bucketList.get(num).add(array[j]);
      }
      int index = 0;
      for (int j = 0; j < bucketList.size(); j++) {
        for (int k = 0; k < bucketList.get(j).size(); k++)
          array[index++] = bucketList.get(j).get(k);
        bucketList.get(j).clear();
      }
    }
    return array;
}
  • 46
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值