排序算法总结
来源:互联网 发布:纵横造价软件多少钱 编辑:程序博客网 时间:2024/05/20 02:53
本文总结了基本的排序算法,包括选择排序,插入排序,冒泡排序,快速排序,堆排序,归并排序,以及,计数排序和基数排序。前六种排序算法是基于比较算法,时间复杂度的下界为o(nlgn),后两种不是基于比较的排序算法,时间复杂度为线性o(n)。
一、堆排序
堆排序的过程是先初始化一个大顶堆,然后堆顶与最后元素交换位置,交换之后再调整为大顶堆。时间复杂度o(nlgn),不稳定排序。
package heap;
/**
* @author typ
*
*/
public class HeapSort {
/**
* 初始化时候,建立一个堆
*
* @param a
*/
public void buildHeap(int[] a) {
int n = a.length;
for (int i = n / 2 - 1; i >= 0; --i) { // (n / 2 - 1), 就是最后一个有子节点的元素
heapify(a, i, n - 1); // 从底往上,不断调整,直到整个数组变成堆
}
}
/**
* 完成从a[i]开始对堆的调整
*
* @param a
* @param i
* @param j
*/
public void heapify(int[] a, int i, int j) {// j代表待排序列的元素个数
int left = i * 2 + 1;
int right = i * 2 + 2;
if (left > j) { // 没有左子树,那么(也不会有右子树),递归停止
return;
}
int large = left; // large 是左子树和右子树中的较大者
if (right <= j && a[left] < a[right]) {
large = right;
}
if (a[i] < a[large]) { // 若根元素比 large 小, 交换根和 large
swap(a, i, large);
heapify(a, large, j); // 此时 large 树 又可能不是堆了, 那就迭代实现large树成堆
}
}
/**
* 堆排序
*
* @param a
*/
public void heapSort(int a[]) {
buildHeap(a);
int size = a.length;
// 每次去第一个元素,即最大元素,与最后元素交换位置。
for (int i = 0; i < size; i++) {
swap(a, 0, (size - i - 1));
heapify(a, 0, size - i - 2);
}
}
public void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
public static void main(String[] args) {
int a[] = { 2, 5, 1, 7, 9, 4, 6, 0 };
HeapSort hSort = new HeapSort();
hSort.heapSort(a);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
非递归的heapify,只改动一处,见红色语句
public void heapify(int[] a, int i, int j) {
while (true) {
int left = i * 2 + 1;
int right = i * 2 + 2;
if (left > j) { // 没有左子树,那么(也不会有右子树),递归停止
break;
}
int large = left; // large 是左子树和右子树中的较大者
if (right <= j && a[left] < a[right]) {
large = right;
}
if (a[i] < a[large]) { // 若根元素比 large 小, 交换根和 large
swap(a, i, large);
i = large;//此处将i值改为large传回,可以代替下面递归语句
// heapify(a, large, j); // 此时 large 树 又可能不是堆了, 那就迭代实现large树成堆
}else {
break;//若根元素比large大,则不需要调整,跳出循环体
}
}
}二、快速排序
快速排序基于分治策略,是由一趟趟的排序组成。选取一个元素为基准元素,经过交换操作,使得该基准元素左边的元素都小于基准元素,右边的元素都大于基准元素,这样就完成了一趟排序。然后递归基准元素左右两部分。最好时间复杂度o(nlgn),最坏时间复杂度o(n2)。不稳定排序。
package quicksort;
/**
* @author typ
*
*/
public class QuickSort {
static int[] quickSort(int a[], int start, int end) {
int i, j;
i = start;
j = end;
int key = a[start];// 取key为序列的第一个元素
while (i < j) {// 整个while循环完成一趟排序
while (i < j && key <= a[j])
// 右侧扫描
j--;
if (i < j) { // 找出第一个比key小的,交换位置
a[i] = a[j];
}
while (i < j && a[i] < key)
// 左侧扫描
i++;
if (i < j) { // 找出第一个比key大的,交换位置
a[j] = a[i];
}
}
a[i] = key;// 注意这步赋值操作,将最后的元素赋key
if (i - start > 1) {
quickSort(a, 0, i - 1);// 递归调用,把key前面的完成排序
}
if (end - j > 1) {
quickSort(a, j + 1, end); // 递归调用,把key后面的完成排序
}
return a;
}
public static void main(String[] args) {
int a[] = { 6, 5, 4, 3, 2, 1 };
int b[] = quickSort(a, 0, 5);
for (int item : b) {
System.out.println(item);
}
}
}
三、插入排序
不断地将后续元素插入到前面已经排好序的元素中。时间复杂度o(n2),稳定排序。
package insertsort;
/**
* @author typ
*
*/
public class InsertSort {
static void insertSort(int a[]) {
int i, j;
for (i = 1; i < a.length; i++) {
int key = a[i];// key的引入,减少交换次数
for (j = i; j > 0 && a[j - 1] > key; j--) {
a[j] = a[j - 1];
}
a[j] = key;
}
}
public static void main(String[] args) {
int a[] = { 6, 5, 4, 3, 2, 1 };
insertSort(a);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
四、选择排序
第i次循环将选出第i大的元素排列到i位置,时间复杂度o(n2),不稳定。
package selectsort;
/**
* @author typ
*
*/
public class SelectSort {
static void selectSort(int a[]) {
int size = a.length, k, tmp;
for (int i = 0; i < size - 1; i++) {
k = i;//用k来记录每次最小的数的位置
for (int j = i; j < size; j++) {
if (a[j] < a[k]) {
k = j;
}
}
if (k != i) {
tmp = a[i];
a[i] = a[k];
a[k] = tmp;
}
}
}
public static void main(String[] args) {
int a[] = { 6, 5, 4, 3, 2, 1 };
selectSort(a);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
五、归并排序
对有序的两个序列进行两两合并,最小的有序序列只含有单个元素。时间复杂度o(nlgn)。稳定排序。
package mergesort;
/**
* @author typ
*
*/
public class MergeSort {
/**
* 递归实现归并排序,调用merge函数(合并两个有序集)
*
* @param a
* @param b
* @param low
* @param high
*/
static void mergeSort(int a[], int b[], int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
mergeSort(a, b, low, mid);
mergeSort(a, b, mid + 1, high);
merge(a, b, low, mid, high);// 合并两个有序集
}
}
/**
* 合并两个有序集
*
* @param a
* @param b
* @param min
* @param mid
* @param max
*/
static void merge(int a[], int b[], int min, int mid, int max) {
int i = min, j = mid + 1, k = min;
while (i <= mid && j <= max) {
if (a[i] < a[j])
b[k++] = a[i++];
else
b[k++] = a[j++];
}
while (i <= mid) {
b[k++] = a[i++];
}
while (j <= max) {
b[k++] = a[j++];
}
for (int m = min; m <= max; m++) {
a[m] = b[m];
}
}
public static void main(String[] args) {
int a[] = { 5, 2, 7, 1, 9, 4, 6, 0 };
int b[] = new int[a.length];
int size = a.length;
mergeSort(a, b, 0, size - 1);
for (int i = 0; i < size; i++) {
System.out.println(a[i]);
}
}
}
六、冒泡排序
第i趟排序,使得第i小的元素排到i处,时间复杂度o(n2)。稳定排序。
package bubblesort;
/**
* @author typ
*
*/
public class BubbleSort {
/**
* 冒泡排序
*
* @param a
*/
static void bubbleSort(int a[]) {
int size = a.length;
for (int i = 1; i < size; i++) {
for (int j = 0; j < size - i; j++) {
if (a[j] > a[j + 1]) {
swap(a, j);
}
}
}
}
/**
* 交换a[j]和a[j+1]
*
* @param a
* @param j
*/
static void swap(int a[], int j) {
int tmp;
tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
public static void main(String[] args) {
int a[] = { 5, 2, 7, 1, 9, 4, 6, 0 };
bubbleSort(a);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
七、计数排序
可以在线性时间内完成排序,要求序列的取值小于某个自然数k。时间复杂度o(n),稳定排序。
package countsort;
/**
* 计数排序,数组的取值范围为0-k,没输入一个元素x,确定小于x的元素的个数,直接将x放到输出数组的指定位置
*
* @author typ
*
*/
public class CountSort {
static void countSort(int a[], int k) {
int c[] = new int[k];// c[i]存放数组a中小于等于i的元素的个数
int b[] = new int[a.length];// b用于存放排好序的a的元素。
for (int i = 0; i < a.length; i++) {// c[i]为数组a中值等于i的元素的个数
c[a[i]] = c[a[i]] + 1;
}
for (int i = 1; i < k; i++) {// c[i]为数组a中值小于等于i的元素的个数
c[i] = c[i] + c[i - 1];
}
for (int i = a.length - 1; i >= 0; i--) {// b中存放排好序的a的元素。为保证排序稳定性,从大到小循环
int index1 = a[i];
int index2 = c[a[i]] - 1;
b[index2] = a[i];// (a中的每个元素在b中的位置已经定下了,在c中保存着,即a[i]应该放在b中的c[a[i]]-1处)
c[index1] -= 1;
}
for (int i = 0; i < b.length; i++) {// 将排好序的数组b赋值给数组a
a[i] = b[i];
}
}
public static void main(String[] args) {
int k = 10;// 代表数组的最大值为k
int a[] = { 3, 1, 4, 4, 2, 1, 7, 8 };
countSort(a, k);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
八、基数排序
通过对元素由低位到高位的按位排序,实现整体的排序。每次的位排序要求排序是稳定的,通常调用计数排序作为基数排序位排序算法。可以实现线性时间o(n)。稳定
package radixsort;
/**
* 基数排序,通过对序列元素每一位的排序,最终形成元素的排序,并不对元素直接进行大小比较。
* 该基数排序中,用到的中间排序算法(对位的排序算法)为计数排序
*
* @author typ
*
*/
public class RadixSort {
/**
* 被调函数-完成计数排序
* @param a
* @param k
* @param num
*/
static void countSort(int a[], int k, int num) {
int c[] = new int[k];// c[i]存放数组a中小于等于i的元素的个数
int b[] = new int[a.length];// b用于存放排好序的a的元素。
for (int i = 0; i < a.length; i++) {// c[i]为数组a中元素的相应位数值等于i的元素的个数
int index = a[i] / num % 10;// index为a[i]的某位
c[index] = c[index] + 1;
}
for (int i = 1; i < k; i++) {// c[i]为数组a中值小于等于i的元素的个数
c[i] = c[i] + c[i - 1];
}
for (int i = a.length - 1; i >= 0; i--) {// b中存放排好序的a的元素。
int index1 = a[i] / num % 10;
int index2 = c[a[i] / num % 10];
b[index2 - 1] = a[i];// (a中的每个元素在b中的位置已经定下了,在c中保存着,即a[i]应该放在b中的c[a[i]]-1处)
c[index1] -= 1;
}
for (int i = 0; i < b.length; i++) {// 将排好序的数组b赋值给数组a
a[i] = b[i];
}
}
/**
* 基数排序的主调函数
* @param a
* @param k
* @param num
*/
static void radixSort(int a[], int k, int num) {
for (int i = 0; i < num; i++) {
countSort(a, k, (int) Math.pow(10, i));// 对a中元素的每一位都调用一次计数排序,且由低位到高位
}
}
public static void main(String[] args) {
int k = 9;// 代表数组的最大值为k
int num = 3;// 表示a数组中数据的最大位数
int a[] = { 23, 231, 524, 114, 332, 121, 227, 448 };
radixSort(a, k, num);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
- 算法--排序算法总结
- 算法:排序算法总结
- 算法:排序算法总结
- 算法-排序算法总结
- 算法-排序算法总结
- 【排序算法】排序算法总结
- 排序算法总结---希尔排序
- 排序算法总结---冒泡排序
- 排序算法总结----快速排序
- 排序算法总结---希尔排序
- 排序算法总结【内排序】
- 排序算法之内排序总结
- 排序算法总结:冒泡排序
- 【排序算法总结】冒泡排序
- 【排序算法总结】选择排序
- 排序算法总结
- 排序算法大总结
- 排序算法总结
- 三分钟理解Java中字符串(String)的存储和赋值原理
- hdu 4547 CD操作
- JBoss 系列六:JBoss 7/WildFly中配置使用JMS消息队列
- CODE 62: Climbing Stairs
- 从一个小侧面对比各大中文搜索引擎的实力
- 排序算法总结
- DevBytes: View Animations
- 模式匹配
- DRLSE 水平集算法总结
- 关于MSHFlexGrid控件的相关操作
- Android环境搭建
- 触发器分为事前触发和事后触发,这两种触发有何区别?语句级触发和行级触发有何区别
- C++类使用默认构造函数时各数据成员的初始化
- (1.1.4)uva 11150 Cola(直叙式模拟)