排序算法

排序分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。我们这里说说八大排序就是内部排序。

分别从算法思想、排序示例、代码实现、算法分析,包括复杂度、稳定性等进行分析,其中算法稳定性是指两个相同的元素,排序前后位置是否发生变化。


一、插入排序(Insertion Sort)


1、直接插入排序

(1)思想

每趟将一个元素,按其关键字值的大小插入到它前面已经排序的子序列中,依次重复,直到插入全部元素

(2)排序示例

第i趟,将元素ai插入到子序列{a0,a1...ai-1}的适当位置,使插入后的子序列仍然是排序的。



(3)代码实现

/**
 * 插入排序算法
 * 
 * @author zzj
 *
 */
public class InsertSort {
	public static void main(String[] args) {
		int[] table = { 3, 5, 1, 7, 9, 0, 3, 8, 2, 6 };
		System.out.println("排序前:");
		System.out.println(Arrays.toString(table));
		insertSort(table);
		System.out.println("排序后:");
		System.out.println(Arrays.toString(table));
	}

	public static void insertSort(int[] table) {
		for (int i = 1; i < table.length; i++) {// i为趟数
			int temp = table[i], j;
			for (j = i - 1; j >= 0 && temp < table[j]; j--) {
				table[j + 1] = table[j];
			}
			table[j + 1] = temp;
		}
	}
}


运行结果:

排序前:
[3, 5, 1, 7, 9, 0, 3, 8, 2, 6]
排序后:
[0, 1, 2, 3, 3, 5, 6, 7, 8, 9]

(4)算法分析

1)时间复杂度

最好是O(n),已排好序;

最坏是(n*2),随机排列和反序排列

2)空间复杂度

0(1)

3)稳定性

稳定


2、二分法插入排序

(1)思想

直接插入排序的每一趟,将一个元素插入到它前面的排序子序列中,其中采用顺序查找算法寻找插入点。此时,子序列是有序的,正好符合二分法查找的要求,因此,用二分法查找代替直接插入排序中的顺序查找。

二分法没有排序,只有查找。所以当找到要插入的位置时。移动必须从最后一个记录开始,向后移动一位。

(2)排序示例

二分查找算法

(3)代码实现

/**
 * 二分插入排序
 * 
 * @author zzj
 *
 */
public class BinarySort {
	public static void main(String[] args) {
		int[] table = { 3, 5, 1, 7, 9, 0, 3, 8, 2, 6 };
		System.out.println("排序前:");
		System.out.println(Arrays.toString(table));
		binarySort(table);
		System.out.println("排序后:");
		System.out.println(Arrays.toString(table));
	}

	public static void binarySort(int[] table) {
		for (int i = 1; i < table.length; i++) {// i为趟数
			int temp = table[i];
			int low = 0, high = i;
			while (low <= high) {
				int mid = (low + high) / 2;
				if (table[mid] < temp) {
					low = mid + 1;
				} else {
					high = mid - 1;
				}
			}
			for (int j = i - 1; j > high; j--) {
				table[j + 1] = table[j];
			}
			table[high + 1] = temp;
		}
	}
}


运行结果

排序前:
[5, 3, 1, 7, 9, 0, 3, 8, 2, 6]
排序后:
[0, 1, 2, 3, 3, 5, 6, 7, 8, 9]

二、交换排序算法

1、冒泡排序(Bubble Sort)

(1)思想

比较相邻两个元素大小,如果反序,则交换。若按升序排序,每趟将数据序列中的最大元素交换到最后位置,就像气泡从水里冒出一样。

(2)排序示例


(3)代码实现

注意:结束条件: 如果一趟扫描没有数据交换,则排序完成

/**
 * 冒泡法
 * 
 * @author zzj
 *
 */
public class BubbleSort {

	public static void main(String[] args) {
		int[] table = { 3, 4, 1, 9, 0, 6, 2, 6, 2 };
		System.out.println("排序前:\n" + Arrays.toString(table));
		int[] table_new = bubbleSort(table);
		System.out.println("排序后:\n" + Arrays.toString(table_new));// 返回的是字符串表示
	}

	public static int[] bubbleSort(int[] table) {
		boolean exchange = true;// 是否交换的标记
		for (int i = 1; i < table.length && exchange; i++) {// i为趟数
			for (int j = 0; j < table.length - i; j++) {// j为每趟需要比较的次数
				exchange = false;// 假设元素都没有交换
				if (table[j] > table[j + 1]) {
					int temp = table[j];
					table[j] = table[j + 1];
					table[j + 1] = temp;
					exchange = true;
				}
			}
		}
		return table;
	}

}


运行结果

排序前:
[5, 3, 1, 7, 9, 0, 3, 8, 2, 6]
排序后:
[0, 1, 2, 3, 3, 5, 6, 7, 8, 9]

(4)算法分析

1)时间复杂度

最好是O(n),排好序,一趟 比较n次

最坏是(n*2),随机排列和反序排列

2)空间复杂度

0(1),交换两个元素

3)稳定性

稳定


2、快速排序(QuickSort)

(1)思想

在数据序列中选择一个元素作为基准值,每趟从数据序列的两端开始交替进行,将小于基准值的元素交换到序列前端,将大于基准值的元素交换到序列后端,介于两者之间的位置则成为基准值的最终位置。同时,序列被划分为两个字序列,再分别对两个子序列进行快速排序,直到子序列长度为1,完成排序。

(2)排序示例




(3)代码实现

/**
 * 快速排序算法
 * @author zzj
 *
 */
public class QuickSort {

	public static void main(String[] args) {
		int[] table = { 3, 5, 1, 8, 0, 3, 5, 2, 7 };
		System.out.println("排序前:");
		System.out.println(Arrays.toString(table));
		quickSort(table, 0, table.length - 1);
		System.out.println("排序后:");
		System.out.println(Arrays.toString(table));
	}

	public static void quickSort(int[] table, int begin, int end) {
		if (begin < end) {//因为是递归调用,所有只有开头位置小于结束位置才执行
			//1、取第一个值为基准值,则空出第一个元素位置i
			int i = begin, j = end;
			int base = table[i];
			while (i != j) {
				//2、将j位置元素与基准值比较,若大,则不移动,循环依次j--
				while (i < j && base <= table[j])
					j--;
				if (i < j) {
					table[i] = table[j];
					i++;
				}
				//3、将i位置元素与基准值比较,若小,则不移动,循环依次i++
				while (i < j && table[i] <= base)
					i++;
				if (i < j) {
					table[j] = table[i];
					j--;
				}
			}
			table[i] = base;
			quickSort(table, begin, j - 1);//递归调用前一个子序列
			quickSort(table, i + 1, end);//递归调用后一个子序列
		}
	}
}

运行结果

排序前:
[3, 5, 1, 8, 0, 3, 5, 2, 7]
排序后:
[0, 1, 2, 3, 3, 5, 5, 7, 8]

(4)算法分析

1)时间复杂度

最好情况:O(n*logn),分成长度相近的两个子序列

最坏情况:O(n2)分成长度差异很大的两个子序列

2)空间复杂度

递归函数。使用栈保存参数,栈占用的空间与递归调用的次数有关:O(logn)

3)稳定性

不稳定


三、选择排序

1、直接选择排序(SelectSort)

(1)思想

(2)排序示例

(3)代码实现

/**
 * 直接选择排序
 * 
 * @author zzj
 *
 */
public class SelectSort {
	public static void main(String[] args) {
		int[] table = { 3, 5, 1, 7, 9, 0, 3, 8, 2, 6 };
		System.out.println("排序前:");
		System.out.println(Arrays.toString(table));
		selectSort(table);
		System.out.println("排序后:");
		System.out.println(Arrays.toString(table));
	}

	public static void selectSort(int[] table) {
		for (int i = 0; i < table.length; i++) {
			int min = i;
			for (int j = i + 1; j < table.length; j++) {
				if (table[min] > table[j]) {
					min = j;
				}
			}
			if (min != i) {
				int temp = table[min];
				table[min] = table[i];
				table[i] = temp;
			}
		}
	}

}


运行结果

(4)算法分析

1)时间复杂度
2)空间复杂度
3)稳定性


四、归并排序(MergeSort)

(1)思想

将两个排序的子序列合并,形成一个排序数据序列,又称两路归并排序。

(2)排序示例


(3)代码实现

/**
 * 归并排序算法
 * @author zzj
 *
 */
public class MergeSort {
	public static void main(String args[]) {
		int[] table = { 52, 5, 3, 76, 63, 36, 15, 78, 24, 32, 43 };
		System.out.println("排序前:");
		System.out.println(Arrays.toString(table));
		mergeSort(table);
		System.out.println("排序后:");
		System.out.println(Arrays.toString(table));
	}

	public static void mergeSort(int[] X) {
		int[] Y = new int[X.length];
		int n = 1;// 子序列长度
		while (n < X.length) {
			mergepass(X, Y, n);// 一趟归并,将X数组中各子序列归并到Y中
			n *= 2;
			if (n < X.length) {
				mergepass(Y, X, n);// 一趟归并,将Y数组中各子序列归并到X中
				n *= 2;
			}
		}
	}

	// 一趟归并
	public static void mergepass(int[] X, int[] Y, int n) {
		for (int i = 0; i < X.length; i += 2 * n) {
			merge(X, Y, i, i + n, n);
		}
	}

	// 一次归并
	public static void merge(int[] X, int[] Y, int m, int r, int n) {
		int i = m, j = r, k = m;
		while (i < r && j < r + n && j < X.length) {
			if (X[i] < X[j]) {
				Y[k++] = X[i++]; // 较小值复制到Y中
			} else {
				Y[k++] = X[j++];
			}
		}
		while (i < r && i < X.length) { // 将前一个子序列剩余元素复制到Y中
			Y[k++] = X[i++];
		}
		while (j < r + n && j < X.length) {// 将后一个子序列剩余元素复制到Y中
			Y[k++] = X[j++];
		}
	}
}


运行结果

排序前:
[52, 5, 3, 76, 63, 36, 15, 78, 24, 32, 43]
排序后:
[3, 5, 15, 24, 32, 36, 43, 52, 63, 76, 78]

(4)算法分析

1)时间复杂度

n个元素归并排序,每趟比较n-1次,数据移动n-1次,进行logn趟,时间复杂度为O(n*logn)

2)空间复杂度

O(n)

3)稳定性

稳定

五、算法对比


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值