排序基础

在这里插入图片描述

///交换排序/
1. 冒泡排序

每次把数组中最小的数字排在对应的位置。
(1) 代码

Java:

public class Sort {
    public static void sort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            boolean sig = false;
            for (int j = nums.length - 1; j > 0; j--) {
                if (nums[j] < nums[j - 1]) {
                    int tem = nums[j - 1];
                    nums[j - 1] = nums[j];
                    nums[j] = tem;
                    sig = true;
                }
            }
            if (sig == false) return ;
        }
    }
}

C++:

//第一种最简单的冒泡排序,下面代码将数组数字递增排列;
class Solution {
public:
	vector<int> bubbleSort(vector<int> vec) {
		int size = vec.size();
		for (int i = size - 1; i > 0; i--) {//每一次遍历是把遍历到的数字放在下面的循环向上冒泡;
			for (int j = size - 1; j > 0; j--) {//每一次遍历是一次比较前后大小
				if (vec[j] < vec[j - 1]) {
					swap(vec[j], vec[j - 1]);
				}
			}
		}
		return vec;
	}
};
//第二种 在判断大小的地方添加本次排序是否有交换的步骤,没有交换说明是递增序列,无须排序,break即可
class Solution {
public:
	vector<int> bubbleSort(vector<int> vec) {
		int size = vec.size();
		bool sp = false;//记录一次从后向前是发生交换;
		for (int i = size - 1; i > 0; i--) {
			for (int j = size - 1; j > 0; j--) {
				if (vec[j] < vec[j - 1]) {
					swap(vec[j], vec[j - 1]);
					sp = true;
				}
			}
			if (sp == false) break;//没有交换说明是递增序列break;
		}
		return vec;
	}
};

(2) 算法性能
时间复杂度:最差情况是数组是倒序的,时间复杂度为O(n^2)。
最好情况是数组是正序的,时间复杂度为O(n)。<遍历一次发现没有交换说明数组是有序的>
平均时间复杂度是O(n^2)。
空间复杂度:原数组操作,空间复杂度为O(1)。
稳定性:只移动后一个比前一个小的,相等的不移动所以稳定性高

2. 快速排序

1.在待排序的元素任取一个元素作为基准(通常选第一个元素,称为基准元素)
2.将待排序的元素进行分块,比基准元素大的元素移动到基准元素的右侧,比基准元素小的移动到作左侧,从而一趟排序过程,就可以锁定基准元素的最终位置
3.对左右两个分块重复以上步骤直到所有元素都是有序的(递归过程)
Java:

public class Sort {
    public static void quickSort(int[] nums, int l, int r) {
        int left = l, right = r;
        if (left >= right) return ;
        int cmp = nums[l];//注!!基准值是每次给的最左边是参数l;
        while (left < right) {//注!!边界条件是left<right
            while (left < right && nums[right] >= cmp) {//注!!需要等于才能越过等于的数;
                right--;//注!!选择左边基准值就要先从右边开始;
            }
            while (left < right && nums[left] <= cmp) {
                left++;
            }
            if (left < right) {
                int tem = nums[left];
                nums[left] = nums[right];
                nums[right] = tem;
            }
        }
        int tem = nums[l];//注!!基准归位;
        nums[l] = nums[left];
        nums[left] = tem;
        quickSort(nums, l, left - 1);
        quickSort(nums, left + 1, r);
    }
}

C++:

class Solution {
public:
	vector<int> quickSort(vector<int> &nums, int left, int right) {
		if (left >= right) return {};//当例如左边只有一个数字返回,或者右面没有数字返回
		int i = left, j = right, cmp = nums[left];//以左边数字作为比较值,就要先移动右边指针
		while (i < j) {
			while (i < j && nums[j] >= cmp) {//j往左移动找比cmp小的值停在这个位置,等于cmp的位置不需要停
				j--;
			}
			while (i < j && nums[i] <= cmp) {//i往右移动找比cmp大的值停在这个位置,因为有等于号所以cmp点不会停下来,等ij相遇才把cmp交换
				i++;
			}
			if (i < j) {
				swap(nums[i], nums[j]);
			}
		}
		swap(nums[i], nums[left]);
		quickSort(nums, left, i - 1);
		quickSort(nums, i + 1, right);
		return nums;
	}
};

(2) 算法性能
在这里插入图片描述时间复杂度:最差情况是数组有序,则递归树是偏树,树的深度是n,每层操作的次数是n次,所以时间复杂度是O(n^2)。
最好情况是数组均匀分布,则递归树两边是平衡的,树的深度是logn,每层操作的次数是n次,所以时间复杂度是0(nlogn)。
空间复杂度:操作是对原数组操作的,主要消耗栈空间,即递归树的深度
稳定性:相等元素可能会因为分区而交换顺序,所以它是不稳定的算法。

///插入排序/

3. 直接插入排序

将第一项作为一个排序序列,后一项往这个数组中插入,数大的就后移
<也可以看成是分段冒泡排序,一开始的数组是一个,加入一个冒泡排序,再加入一个冒泡排序>
<每次前面的数组都是有序的,优化可以在插入的时候用二分>
(1) 代码

class Solution {
public:
	vector<int> directInsert(vector<int> vec) {
		for (int i = 1; i < vec.size(); i++) {//从第二个数字开始,这个数字遍历的是数组后面的数字往已经排序的数组中插入
			int com = vec[i];//要插入的这个数字
			for (int j = i - 1; j >= 0; j--) {//遍历已经排序的数组往其中插入
				if (com < vec[j]) {//要插入的数字com只要比已经排序的数组中遍历到的这个数字小,就把排序数组中的这个数字后移
					swap(vec[j+ 1], vec[j]);
				}
			}
		}
		return vec;
	}
};

(2) 算法性能
时间复杂度:最差情况是数组倒叙,先遍历数组,每次插入都需要移动前面的元素,时间复杂度为O(n^2)。
最好情况是数组正序,只需遍历一次,每次插入不需要移动前面的元素,时间复杂度为O(n)。
平均是O(N^2)。
稳定性:只移动比较小的,相等的不移动所以稳定。

///选择排序/

4. 堆排序
堆:父节点大于子节点的完全二叉树。
Java:

public class Sort {
    public static void heapSort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            heapInsert(nums, i);
        }
        for (int i = nums.length - 1; i >= 0; i--) {
            int tem = nums[0];
            nums[0] = nums[i];
            nums[i] = tem;
            heapify(nums, 0, i);
        }
    }
    public static void heapInsert(int[] nums, int idx) {
        int far = (idx - 1) / 2;
        while (nums[idx] > nums[far]) {
            int tem = nums[idx];
            nums[idx] = nums[far];
            nums[far] = tem;
            idx = far;
            far = (far - 1) / 2;
        }
    }
    public static void heapify(int[] nums, int far, int heapSize) {
        while (far < heapSize) {
            int lefChild = 2 * far + 1, rigChild = 2 * far + 2;
            int max = far;
            if (lefChild < heapSize && nums[lefChild] > nums[max])//注!!与max比较!
                max = lefChild;//注!!max、far都是下标,比较的是数字;
            if (rigChild < heapSize && nums[rigChild] > nums[max])
                max = rigChild;
            if (max == far) break;//注!!父节点没有变中断循环
            else {
                int tem = nums[max];
                nums[max] = nums[far];
                nums[far] = tem;
                far = max;
            }
        }
    }
}

Cpp:

class Solution {
public:
    void heapSort(vector<int> &nums) {
        for (int i = 0; i < nums.size(); i++) {
            heapInsert(nums, i);
        }
        for (int i = nums.size() - 1; i >= 0; i--) {
            swap(nums[0], nums[i]);//注!!每次都要把最大值交换后再heapify
            heapify(nums, 0, i);//注!!i表示heapSize
        }
    }
private:
    void heapInsert(vector<int> &nums, int index) {
        int far = (index - 1) / 2;
        while (nums[index] > nums[far]) {//注!!往上走far不会越界;
            swap(nums[far], nums[index]);
            index = far;
            far = (far - 1) / 2;//注!!far是往上走的,所以不会超过0;
        }                     //判断条件index和far相等时可以停止;
    }
    void heapify(vector<int> &nums, int far, int heapSize) {
        while (far <= heapSize - 1) {//注!!往下走far需要考虑越界;
            int left = 2 * far + 1, right = 2 * far + 2;
            int max = far;
            if (left <= heapSize - 1 && nums[left] > nums[max]) {//注!!与max比较
                max = left;//注!!max只用来标记
            }
            if (right <= heapSize - 1 && nums[right] > nums[max]) {
                max = right;
            }
            if (max == far) {//注!!==
                break;    //注!!之前如果far就是max或者左或右节点越界了都不需要继续往下了
            }
            else {
                swap(nums[max], nums[far]);
                far = max;
            }
        }
    }
};

(2) 算法性能
时间复杂度:heapInsert(建堆)的时间复杂度是O(n) <每加入一个节点之和二叉树层数有关:log1 + log2 + … + logN - 1>
heapify(调整为堆)的时间复杂度是O(nlogn) <一共heapify n次,每次只与一条路径相关>
综上时间复杂度是O(nlogn)
稳定性:相等元素可能会被交换位置,所以是不稳定的。

///归并排序/
5. 归并排序
分解(Divide):将n个元素分成个含n/2个元素的子序列。
解决(Conquer):用合并排序法对两个子序列递归的排序。
合并(Combine):合并两个已排序的子序列已得到排序结果。

java:

public class Sort<kClosest> {
    public void mergeSort(int[] nums, int left, int right) {
        if (left >= right) return ;
        int middle = left + (right - left) / 2;
        mergeSort(nums, left, middle);
        mergeSort(nums, middle + 1, right);
        merge(nums, left, middle, right);
    }
    public void merge(int[] nums, int left, int middle, int right) {
        int[] tem = new int[right - left + 1];
        int p1 = left, p2 = middle + 1;
        int i = 0;//先填tem
        while (p1 <= middle && p2 <= right) {
            tem[i++] = nums[p1] < nums[p2] ? nums[p1++] : nums[p2++];
        }
        while (p1 <= middle) {//两个while只发生一个
            tem[i++] = nums[p1++];
        }
        while (p2 <= right) {
            tem[i++] = nums[p2++];
        }
		//再填nums
        for (i = 0; i < tem.length; i++) {
            nums[left + i] = tem[i];//注!!是left + i;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值