冒泡排序(稳定)
流程
1 从头开始比较每一对相邻元素,如果第1个比第2个大,就交换它们的位置
执行完一轮后,最未尾那个元素就是最大的元素。
2 忽略1中曾经找到的最大元素,重复执行步骤1,直到全部元素有序。
public class BubbleSort1<T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort1() {
for (int end = array.length - 1; end > 0; end--) {
for (int begin = 1; begin <= end; begin++) {
if (cmp(begin, begin - 1) < 0) {
swap(begin, begin - 1);
}
}
}
}
}
/**
* 优化 如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数
*/
public class BubbleSort3<T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort2() {
for (int end = array.length - 1; end > 0; end--) {
// 设为1是因为:如果这轮遍历一次交换都没进行
// 则说明序列后面已经有序, 则 end = sortedIndex = 1
// end--后等于0,会直接跳出循环
int sortedIndex = 1;
for (int begin = 1; begin <= end; begin++) {
if (cmp(begin, begin - 1) < 0) {
swap(begin, begin - 1);
// 记录最后1次交换的位置,减少比较次数
sortedIndex = begin;
}
}
end = sortedIndex;
}
}
}
protected int cmp(int i1, int i2) {
return ((i1-i2 > 0) ? 1:0;);
protected void swap(int i1, int i2) {
swapCount++;
T tmp = array[i1];
array[i1] = array[i2];
array[i2] = tmp;
}
选择排序(不稳定)
流程
1 从序列中找出最大的那个元素,然后与最未尾的元素交换位置
执行完一轮后,最未尾的那个元素就是最大的元素。
2 忽略1中曾经找到的最大元素,重复执行步骤1。
public class SelectionSort<T extends Comparable<T>> extends Sort<T> {
@Override
protected void sort3() {
for (int end = array.length - 1; end > 0; end--) {
int max = 0;
for (int begin = 1; begin <= end; begin++) {
if (cmp(max, begin) < 0) {
max = begin;
}
}
swap(max, end);
}
}
}
堆排序(不稳定)
流程
1 对序列进行原地建堆
2 依次取出堆中最大值,并删除
3 重复执行以2操作,直到堆的元素数量为 1
交换堆顶元素与尾元素
堆的元素数量减 1
对 0 位置进行 1 次 下滤
/**
* 堆排序
*/
public class HeapSort<T extends Comparable<T>> extends Sort<T> {
private int heapSize; // 堆大小
@Override
protected void sort4() {
// 原地建堆(自下而上的下滤)
heapSize = array.length;
for (int i = (heapSize >> 1) - 1; i >= 0; i--) {
siftDown(i);
}
while (heapSize > 1) {
// 交换堆顶元素和尾部元素
swap(0, --heapSize);
// 对0位置进行siftDown(恢复堆的性质)
siftDown(0);
}
}
private void siftDown(int index) {
T element = array[index];
int half = heapSize >> 1;
while (index < half) { // index必须是非叶子节点
// 默认是左边跟父节点比
int childIndex = (index << 1) + 1;
T child = array[childIndex];
int rightIndex = childIndex + 1;
// 右子节点要存在, 并且比左子节点大
if (rightIndex < heapSize &&
cmp(array[rightIndex], child) > 0) {
child = array[childIndex = rightIndex];
}
// 大于等于子节点
if (cmp(element, child) >= 0) break;
array[index] = child;
index = childIndex;
}
array[index] = element;
}
}
插入排序(稳定)
流程
1 在执行过程中,插入排序会将序列分为 2 部分,头部是已经排好序的,尾部是待排序的。
2 从头开始扫描每一个元素,每当扫描到一个元素,就将它插入到头部合适的位置,使得头部数据依然保持有序。
/**
* 插入排序-优化
* 交换 -> 挪动
*/
public class InsertionSort2 <T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort() {
for(int begin = 1; begin < array.length; begin++){
int cur = begin;
T v = array[begin]; // 将待插入元素备份
while(cur > 0 && cmp(v, array[cur-1]) < 0){
// 头部有序数据中比待插入元素大的,都朝尾部方向挪动1个位置
array[cur] = array[cur - 1];
cur--;
}
array[cur] = v; // 将待插入元素放到最终的合适位置
}
}
}
二分搜索
/**
* 插入排序-优化2
* 二分搜索进行优化
*/
public class InsertionSort3 <T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort() {
for(int begin = 1; begin < array.length; begin++){
// 将遍历到的元素插入到前面已经排好序的序列中
insert(begin, search(begin)); //search() 查找到要插入的位置
}
}
/**
* 将source位置的元素插入到dest位置
*/
private void insert(int source, int dest){
T v = array[source]; // 备份要插入的元素
// 将 [insertIndex, begin)范围内的元素往右边挪动一个单位
for(int i = source; i > dest; i--){
array[i] = array[i - 1];
}
array[dest] = v;
}
/**
* 利用二分搜索找到index位置元素的待插入位置
* 已经排好序数组的区间范围是[0,index)
*/
private int search(int index){
int begin = 0;
int end = index;
while(begin < end){
int mid = (begin + end) >> 1;
if(cmp(array[index], array[mid]) < 0){
end = mid;
}else{
begin = mid + 1;
}
}
return begin;
}
}
归并排序(稳定)
@SuppressWarnings("unchecked")
public class MergeSort <T extends Comparable<T>> extends Sort<T> {
private T[] leftArray;
@Override
protected void sort() {
// 准备一段临时的数组空间, 在merge操作中使用
leftArray = (T[])new Comparable[array.length >> 1];
sort(0, array.length);
}
/**
* 对 [begin, end) 范围的数据进行归并排序
*/
private void sort(int begin, int end){
if(end - begin < 2) return; // 至少要2个元素
int mid = (begin + end) >> 1;
sort(begin, mid); // 归并排序左半子序列
sort(mid, end); // 归并排序右半子序列
merge(begin, mid, end); // 合并整个序列
}
/**
* 将 [begin, mid) 和 [mid, end) 范围的序列合并成一个有序序列
*/
private void merge(int begin, int mid, int end){
int li = 0, le = mid - begin; // 左边数组(基于leftArray)
int ri = mid, re = end; // 右边数组(array)
int ai = begin; // array的索引
// 备份左边数组到leftArray
for(int i = li; i < le; i++){
leftArray[i] = array[begin + i];
}
// 如果左边还没有结束
while(li < le){ // li == le 左边结束, 则直接结束归并
if(ri < re && cmp(array[ri], leftArray[li]) < 0){ // cmp改为<=0会失去稳定性
array[ai++] = array[ri++]; // 右边<左边, 拷贝右边数组到array
}else{
array[ai++] = leftArray[li++]; // 左边<=右边, 拷贝左边数组到array
}
}
}
}
快速排序(不稳定)
public class QuickSort<T extends Comparable<T>> extends Sort<T> {
@Override
protected void sort() {
sort(0, array.length);
}
/**
* 对 [begin, end) 范围的元素进行快速排序
*/
private void sort(int begin, int end){
if(end - begin < 2) return;
// 确定轴点位置 O(n)
int mid = pivotIndex(begin, end);
// 对子序列进行快速排序
sort(begin, mid);
sort(mid + 1, end);
}
/**
* 构造出 [begin, end) 范围的轴点元素
* @return 轴点元素的最终位置
*/
private int pivotIndex(int begin, int end){
// 随机选择轴点元素
swap(begin, begin + (int)Math.random()*(end - begin));
// 备份begin位置的元素
T pivot = array[begin];
// end指向最后一个元素
end--;
while(begin < end){
while(begin < end){ // 从右往左扫描
if(cmp(pivot, array[end]) < 0){ // 右边元素 > 轴点元素
end--;
}else{ // 右边元素 <= 轴点元素
array[begin++] = array[end];
break;
}
}
while(begin < end){ // 从左往右扫描
if(cmp(pivot, array[begin]) > 0){ // 左边元素 < 轴点元素
begin++;
}else{ // 左边元素 >= 轴点元素
array[end--] = array[begin];
break;
}
}
}
// 将轴点元素放入最终的位置
array[begin] = pivot;
// 返回轴点元素的位置
return begin; // begin==end
}
}
希尔排序(不稳定)
public class ShellSort <T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort() {
// 根据元素数量算出步长序列
List<Integer> stepSequence = shellStpSequence();
// 按步长序列划分进行排序
for (Integer step : stepSequence) {
sort(step); // 按step进行排序
}
}
/**
* 分成step列进行排序
*/
private void sort(int step){
// col: 第几列, column的简称
for(int col = 0; col < step; col++){
// 插入排序对每一列进行排序
for(int begin = col + step; begin < array.length; begin += step){
// col、col+step、col+2*step、col+3*step
int cur = begin;
while(cur > col && cmp(cur, cur - step) < 0){
swap(cur, cur - step);
cur -= step;
}
}
}
}
/**
* 希尔本人提出的步长序列
*/
public List<Integer> shellStpSequence(){
List<Integer> stepSequence = new ArrayList<>();
int step = array.length;
while((step >>= 1) > 0){
stepSequence.add(step);
}
return stepSequence;
}
}
计数排序(稳定)
public class CountingSort extends Sort<Integer>{
@Override
protected void sort() {
// 找出最值
int max = array[0];
int min = array[0];
for(int i = 0; i < array.length; i++){
if(array[i] < min){
min = array[i];
}
if(array[i] > max){
max = array[i];
}
}
// 开辟内存空间,存储次数
int[] counts = new int[max - min + 1];
// 统计每个整数出现的次数
for(int i = 0; i < array.length; i++){
counts[array[i] - min]++;
}
// 累加次数
for(int i = 1; i < counts.length; i++){
counts[i] += counts[i - 1];
}
// 从后往前遍历元素,将它放到有序数组中的合适位置
int[] newArray = new int[array.length];
for(int i = array.length - 1; i >= 0; i--){
newArray[--counts[array[i] - min]] = array[i];
}
// 将有序数组赋值到array
for (int i = 0; i < newArray.length; i++) {
array[i] = newArray[i];
}
}
}