排序算法
比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
选择排序、冒泡排序、插入排序、希尔排序、归并排序、堆排序、快速排序
非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序
计数排序、桶排序、基数排序
相关概念
- 稳定:如果a原本在b前面,而a==b,排序之后a仍然在b的前面。
- 不稳定:如果a原本在b的前面,而a==b,排序之后 a 可能会出现在 b 的后面。
- 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
- 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
排序接口
public abstract class Sort {
public int[] arr;
public Sort(){};
public Sort(int[] arr){
this.arr = new int[arr.length];
for(int i = 0; i < arr.length; i++){
this.arr[i] = arr[i];
}
}
public abstract void sort();
public void swap(int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
选择排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕
public class SelectionSort extends Sort{
public SelectionSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
swap(i,j);
}
}
}
}
}
冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成
public class BubbleSort extends Sort{
public BubbleSort(int[] arr){
super(arr);
}
@Override
public void sort() {
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr.length - i - 1; j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
}
插入排序
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
public class InsertionSort extends Sort{
public InsertionSort(int arr[]){
super(arr);
}
@Override
public void sort() {
for(int i = 1; i <arr.length; i++){
int e = arr[i];
int j = 0;
for(j = i; j > 0 && arr[j - 1] > e; j--){
arr[j] = arr[j - 1];
}
arr[j] = e;
}
}
}
希尔排序
public class ShellSort extends Sort{
public ShellSort(int arr[]){
super(arr);
}
@Override
public void sort() {
int len = arr.length;
for(int gap = len / 2; gap > 0; gap = gap / 2){
for(int i = gap; i < len; i++){
int e = arr[i];
int j = i;
while( j- gap >= 0 && arr[j - gap] > e){
arr[j] = arr[j - gap];
j = j - gap;
}
arr[j] = e;
}
}
}
}
归并排序
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2路归并
public class MergeSort extends Sort{
public MergeSort(int arr[]){
super(arr);
}
@Override
public void sort() {
mergeSort(0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private void mergeSort(int L, int R) {
if(L >= R){
return;
}
int mid = (L + R) / 2;
mergeSort(L, mid);
mergeSort(mid + 1, R);
if(arr[mid] > arr[mid + 1])
merge(L, mid, R);
}
private void merge(int L, int mid, int R) {
int[] aux = new int[R - L +1];
for(int k = L; k <= R; k++){
aux[k - L] = arr[k];
}
int i = L;
int j = mid + 1;
for(int k = L; k <= R; k++){
if(i > mid){
arr[k] = aux[j - L];
j++;
}else if(j > R){
arr[k] = aux[i - L];
i++;
}else if(aux[i - L] < aux[j - L]){
arr[k] = aux[i - L];
i++;
}else{
arr[k] = aux[j - L];
j++;
}
}
}
}
快速排序
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,比另一部分的关键字大,则可分别对这两部分记录继续进行排序,以达到整个序列有序
单路快速排序
public class QuickSort extends Sort{
public QuickSort(int arr[]){
super(arr);
}
@Override
public void sort() {
quickSort(0, arr.length - 1);
}
private void quickSort(int L, int R) {
if(L >= R){
return;
}
int p = partition(L ,R);
quickSort(L, p - 1);
quickSort(p + 1, R);
}
private int partition(int L, int R) {
swap(L, (int) (Math.random() * (R - L + 1) + L));
int v = arr[L];
int j = L;
for(int i = L + 1; i <= R; i++){
if(arr[i] > v){
swap(j + 1 , i);
j++;
}
}
swap(L, j);
return j;
}
}
双路快速排序
public class QuickSort02 extends Sort{
public QuickSort02(int arr[]){
super(arr);
}
@Override
public void sort() {
quickSort(0, arr.length - 1);
System.out.println(Arrays.toString(arr));
System.out.println(arr);
}
private void quickSort(int L, int R) {
if(L >= R){
return;
}
int p = partition(L ,R);
quickSort(L, p - 1);
quickSort(p + 1, R);
}
private int partition(int L, int R) {
swap(L, (int) (Math.random() * (R - L + 1) + L));
int v = arr[L];
int i = L + 1;
int j = R;
while(true){
while(arr[i] < v && i <= R){
i++;
}
while(j >= L + 1 && arr[j] > v){
j--;
}
if(i > j){
break;
}
swap(i, j);
i++;
j--;
}
swap(L, j);
return j;
}
}
三路快速排序
public class QuickSort03 extends Sort{
public QuickSort03(int arr[]){
super(arr);
}
@Override
public void sort() {
quickSort(0, arr.length - 1);
System.out.println(Arrays.toString(arr));
System.out.println(arr);
}
private void quickSort(int L, int R) {
if(L >= R){
return;
}
swap(L, (int) (Math.random() * (R - L + 1) + L));
int v= arr[L];
int lt = L;
int gt = R + 1;
int i = L + 1;
while(i < gt) {
if (arr[i] < v) {
swap(i, lt + 1);
lt++;
i++;
} else if (arr[i] > v) {
swap(i, gt - 1);
gt--;
} else {
i++;
}
}
swap(L, lt);
quickSort(L, lt - 1);
quickSort(gt, R);
}
}
计数排序
public class CountingSort extends Sort{
public CountingSort(int arr[]){
super(arr);
}
@Override
public void sort() {
int max = arr[0];
int min = arr[0];
for(int i = 0; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
if(arr[i] < min){
min = arr[i];
}
}
int counts[] = new int [max - min + 1];
for(int i = 0; i < arr.length; i++){
counts[arr[i] - min]++;
}
int k = 0;
for(int index = 0; index < counts.length; index++){
for(int count = 0; count < counts[index]; count++){
int num = index + min;
arr[k++] = num;
}
}
}
}
桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序
public class BucketSort extends Sort{
public BucketSort(int arr[]){
super(arr);
}
@Override
public void sort() {
int max = arr[0];
int min = arr[0];
for(int i = 1; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
if(arr[i] < min){
min = arr[i];
}
}
int buckeNum = (max - min) / arr.length + 1;
ArrayList<Integer> list[] = new ArrayList[buckeNum];
for(int i = 0; i <list.length; i++){
list[i] = new ArrayList<>();
}
for(int i = 0; i < arr.length; i++){
list[(arr[i] - min) / arr.length].add(arr[i]);
}
for(int i = 0; i < list.length; i++){
list[i].sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
}
int index = 0;
for(int i = 0; i < list.length; i++){
for(int j = 0; j < list[i].size(); j++){
arr[index++] = list[i].get(j);
}
}
}
}
基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前
public class RadixSort extends Sort{
public RadixSort(int arr[]){
super(arr);
}
@Override
public void sort() {
int radix = getRadix();
LinkedList<Integer>[] list = new LinkedList[10];
for(int i = 0; i < list.length; i++){
list[i] = new LinkedList<>();
}
for(int r = 1; r <= radix; r++){
for(int i = 0; i < arr.length; i++){
list[getindex(arr[i], r)].offer(arr[i]);
}
int index = 0;
for(int i = 0; i < list.length; i++){
while(!list[i].isEmpty()){
arr[index++] = list[i].poll();
}
}
}
}
private int getindex(int num, int r) {
int ret = 0;
for(int i = 1; i <= r; i++){
ret = num % 10;
num /= 10;
}
return ret;
}
private int getRadix() {
int max = arr[0];
for(int i = 1; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
}
return (max + "").length();
}
}