各算法时间、空间复杂度和稳定性:::
来源:https://blog.csdn.net/shihuboke/article/details/79387523
如果某个排序算法能保证:对于待排序的数组中重复元素排序后相对顺序不变,就称这种排序算法是稳定的。否则不稳定
1.快速排序
思路:
确定一个数为分界线,在其左边的全比它小,在它右边的全比他大;
再分别对其左右两边进行快排(递归)
代码:
public static void quickSort(int[] arr, int start, int end){
if(start >= end)
return;
int left = start, right = end;
int key = arr[left];//一般取第一个元素作为分界线
//当left = right, arr[left] = key
while(left < right){
//先从后往前找
while(arr[right] >= key && left < right){//从后往前找第一个小于key的数
right--;
}
//将更小的数放到右边
arr[left] = arr[right];
while(arr[left] <= key && left < right){//从前往后找第一个大于key的数
left++;
}
//将更大的数放到左边
arr[right] = arr[left];
}
arr[left] = key;//分界线,left左边都是比key小的,left右边都是比key大的
quickSort(arr, start, left - 1); // 递归调用
quickSort(arr, left + 1, end);
}
主函数:
class quickSort{
public static void main(String[] args){
//int arr[] = new int[]{3,13,3,7,9,122344,4656,34,34,4656,5,6,7,8,9,343,57765,3,12321};
int arr[] = new int[]{13,57765,2,2,2,12321};
int len = arr.length-1;
quickSort(arr,0,len);
for (int i:arr) {
System.out.print(i+"\t");
}
}
}
时间复杂度:
最优复杂度:O(nlog(n))
平均复杂度:O(nlog(n))
最差复杂度:O(n^2)如,降序的数组要求排成升序,这种情况和冒泡排序一样
空间复杂度:O(log(n))
稳定性:不稳定,从后往前找,找到一个比key小的,就交换,相同元素的顺序必然交换,如{3,2,2,5}
2.插入排序
思路:
类似打扑克牌的时候排序,将待插入的牌插入到已有序的牌中
第一次for循环中起始位 i = 1是从第二张牌开始插入操作
第二次for循环的起始位是 j = i 就是每次把这一位置的元素插入,从后往前比较,因为本次排序前只有前 i-1 位是有序的
代码:
public static void insertSort(int[] arr){
int len = arr.length;
for(int i = 1; i < len; i++){//要插入的牌
for(int j = i; j > 0 && arr[j] < arr[j - 1]; j--){//找要插入的位置,判断条件这么写,最优时间复杂度才是O(n)
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
复杂度分析:
时间复杂度:
最优时间复杂度:O(n),已经有序
最差时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
空间复杂度:O(1)
3.冒泡排序:
思路:
按位置重复遍历,两两比较
注意第二个循环的起始位是 j = 0,而终止位是len - 1 - i,这样的目的是每轮排序都使一个最大元素沉底(数组最后端,而这个元素则是本轮排序中的最大值)
它重复地走访过要排序的数列,一次比较相邻两个元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。
代码:
public static void bubbleSort(int[] arr){
int len = arr.length;
for(int i = 0; i < len - 1; i++){
for(int j = 0; j < len - 1 - i; j++){
if(arr[j] > arr[j + 1]){//升序排列,把这个条件判断放在for循环里,就能实现最优时间复杂度为O(n)
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
注意,java不可以定义一个交换函数,因为Java中不能直接操作指针变量,int、double、float等基本类型函数传参的时候都是值传递,也就是传入函数的只是原来变量的一个副本,所以在函数中交换是副本,并达不到交换的目的。
参考:https://blog.csdn.net/cflys/article/details/71102021
不能交换,基本类型传参时是值传递的
public static void swap(int a, int b){
int temp = a;
a = b;
b = temp;
}
复杂度分析:
时间复杂度:
最优时间复杂度:O(n),已经有序
最差时间复杂度:O(n(n-1)/2) = O(n^2)
平均时间复杂度:O(n^2)
空间复杂度:O(1)
4.选择排序:
思路:
每轮找到最小值,放在已排序列末尾
它的工作原理如下。
首先在还没有排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
代码:
public static void selectSort(int[] arr){
int len = arr.length;
for(int i = 0; i < len; i++){
int minIndex = i;//找到此轮最小的索引
for(int j = i; j < len; j++){
if(arr[j] < arr[minIndex]){//有等于号,不稳定,没有等于号,稳定
minIndex = j;
}
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
复杂度分析:
时间复杂度:
最优时间复杂度:O(n^2)
最差时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
空间复杂度:O(1)
5.归并排序
将数组不断二分,直到最小长度为1,再进行归并操作
代码:
//归并排序
public static int[] mergeSort(int[] arr, int start, int end) {
if (start >= end)
return new int[]{arr[start]};
int mid = start + (end - start) / 2;
int[] leftArr = mergeSort(arr, start, mid); //左有序数组
int[] rightArr = mergeSort(arr, mid + 1, end); //右有序数组
int[] ans = new int[leftArr.length + rightArr.length]; //新有序数组
int index = 0, leftStart = 0, rightStart = 0;
while (leftStart < leftArr.length && rightStart < rightArr.length) {
ans[index++] = leftArr[leftStart] < rightArr[rightStart] ? leftArr[leftStart++] : rightArr[rightStart++];
}
//左右有序数组中可能有一个剩余,把剩余的元素加上
while (leftStart < leftArr.length)
ans[index++] = leftArr[leftStart++];
while (rightStart < rightArr.length)
ans[index++] = rightArr[rightStart++];
return ans;
}
6.堆排序
public class HeapSort implements IArraySort {
@Override
public int[] sort(int[] sourceArray) throws Exception {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
int len = arr.length;
buildMaxHeap(arr, len);
for (int i = len - 1; i > 0; i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0, len);
}
return arr;
}
private void buildMaxHeap(int[] arr, int len) {
for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
heapify(arr, i, len);
}
}
private void heapify(int[] arr, int i, int len) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int largest = i;
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest, len);
}
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
最大堆
public class HeapSort1 {
public static void main(String[] args) {
int[] a = new int[]{16, 25, 7, 32, 6, 9};
heapSort(a);
System.out.println(Arrays.toString(a));
}
/**
* 构造大顶堆
* @param arr 待调整数组
* @param size 调整多少
* @param index 调整哪一个 最后一个叶子节点的父节点开始调整
*/
public static void maxHeap(int arr[], int size, int index) {
//左子节点
int leftNode = 2 * index + 1;
//右子节点
int rightNode = 2 * index + 2;
int max = index;//假设自己最大
//分别比较左右叶子节点找出最大
if(leftNode < size && arr[leftNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成leftNode并且递归需要限定范围为数组长度,
max = leftNode;//将最大位置改为左子节点
}
if(rightNode < size && arr[rightNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成rightNode
max = rightNode;//将最大位置改为右子节点
}
//如果不相等就需要交换
if(max != index) {
int tem = arr[index];
arr[index] = arr[max];
arr[max] = tem;
//如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
maxHeap(arr, size, max);//位置为刚才改动的位置;
}
}
/**
* 需要将最大的顶部与最后一个交换
* @param arr
*/
public static void heapSort(int arr[]) {
int start = (arr.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
for(int i = start; i>=0; i--) {
maxHeap(arr, arr.length, i);
}
//最后一个跟第一个进行调整
for(int i = arr.length-1; i>0; i--) {//因为数组从零开始的,所以最后一个是数组长度减一
int temp = arr[0];//最前面的一个
arr[0] = arr[i];//最后一个
arr[i] = temp;
//调整后再进行大顶堆调整
maxHeap(arr, i, 0);
}
}
}
最小堆
public class HeapSortMin {
public static void main(String[] args) {
int[] a = new int[]{16, 25, 7, 32, 6, 9};
heapSort(a);//小顶堆
System.out.println(Arrays.toString(a));
}
/**
* 构造小顶堆
* @param arr 待调整数组
* @param size 调整多少
* @param index 调整哪一个 最后一个叶子节点的父节点开始调整
*/
public static void minHeap(int arr[], int size, int index) {
//左子节点
int leftNode = 2 * index + 1;
//右子节点
int rightNode = 2 * index + 2;
int min = index;//假设自己最小
//分别比较左右叶子节点找出最小
if(leftNode < size && arr[leftNode] < arr[min]) {//如果左侧叶子节点小于min则将最小位置换成leftNode并且递归需要限定范围为数组长度,
min = leftNode;//将最小位置改为左子节点
}
if(rightNode < size && arr[rightNode] < arr[min]) {//如果左侧叶子节点小于min则将最小位置换成rightNode
min = rightNode;//将最小位置改为右子节点
}
//如果不相等就需要交换
if(min != index) {
int tem = arr[index];
arr[index] = arr[min];
arr[min] = tem;
//如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
minHeap(arr, size, min);//位置为刚才改动的位置;
}
}
/**
* 需要将最小的顶部与最后一个交换
* @param arr
*/
public static void heapSort(int arr[]) {
int start = (arr.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
for(int i = start; i>=0; i--) {
minHeap(arr, arr.length, i);
}
//最后一个跟第一个进行调整
for(int i = arr.length-1; i > 0; i--) {
int temp = arr[0];//最前面的一个
arr[0] = arr[i];//最后一个
arr[i] = temp;
//调整后再进行小顶堆调整
minHeap(arr, i, 0);
}
}
}