///交换排序/
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;
}
}
}