一、冒泡排序
原理: 第一遍从0-n-1遍历,比较相邻元素,最后一个位置确定;第二遍从0-n-2遍历,比较相邻元素,倒数第二个位置被确定;一直遍历直到每个位置都被确定。
数据是反序时,耗费时间最长O(n²);
数据是正序时,耗费时间最短O(n)
是一种稳定的排序算法。
/* 力扣代码 */
class Solution{
public:
vector<int> sortArray(vector<int>& nums) {
int len = (int)nums.size();
for(int i=0;i<=len-1;i++)
{
for(int j=0;j<len-1-i;j++)
{
if(nums[j] > nums[j+1])
{
swap(nums[j],nums[j+1]);
}
}
}
return nums;
}
};
二、选择排序
原理: 遍历所有的数据,先在数据中找出最大或最小的元素,放到序列的起始,再从余下的数据中继续寻找最大或最小的元素,依次放到序列中,直到所有有序。
平均时间复杂度为O(n²)
是一种不稳定的排序算法。
/* 力扣代码 */
class Solution{
public:
vector<int> sortArray(vector<int>& nums)
{
int len = nums.size();
for(int i=1;i<len;i++)
{
int k = i-1;
for(int j=i;j<len;j++)
{
if(nums[j] < nums[k])
{
k = j;
}
}
if(k != i-1)
{
swap(nums[k],nums[i-1]);
}
}
return nums;
}
};
三、快速排序
原理: 从数组中选择一个元素作为中轴元素,一般可选第一个,把数组中小于中轴的元素放在其左边,大于中轴的元素放在其右边,之后再递归快速排序中轴左右两边的序列直到完成。
最理想的情况是,整个算法的时间复杂度为O(n logn)
最坏的情况是,整个排序算法的时间复杂度为O(n²)
是一种不稳定的排序算法。
/* 力扣代码 */
class Solution{
public:
int quickSort(vector<int>& arr,int left,int right)
{
if(left >= right)
{
return 0;
}
int middle = arr[left];
int begin=left,end=right;
int start = left;
while(begin < end)
{
while(begin < end && arr[end] >= middle)
{
end--;
}
while(begin < end && arr[begin] <= middle)
{
begin++;
}
if(begin != end)
{
swap(arr[begin],arr[end]);
}
}
swap(arr[start],arr[begin]);
quickSort(arr,left,begin-1);
quickSort(arr,begin+1,right);
return 0;
}
vector<int> sortArray(vector<int>& nums) {
srand((unsigned)time(NULL));
quickSort(nums,0,(int)nums.size() - 1);
return nums;
}
};
四、堆排序
原理: 将数组当成二叉树构建,且必须满足每一个节点的值都必须大于或等于左右子节点的值。之后将根节点与最后的一个节点交换位置,再对除了最后一个节点外重新排序;一直重复这个步骤,直到排完。
平均时间复杂度为O(n logn),是一种不稳定的排序算法。
/* 力扣代码 */
class Solution{
public:
void max_heap(vector<int>& arr,int start,int end)
{
int dad = start;
int son = dad*2 + 1;
while(son <= end)
{
if(son+1 <= end && arr[son] < arr[son+1])
{
son++;
}
if(arr[dad] > arr[son])
{
return;
}
if(son <= end && arr[son] > arr[dad])
{
swap(arr[son],arr[dad]);
dad = son;
son = dad*2+1;
}
}
}
void heap_sort(vector<int>&arr,int len)
{
for(int i=len/2-1;i>=0;i--)
{
max_heap(arr,i,len-1);
}
for(int i=len-1;i>0;i--)
{
swap(arr[0],arr[i]);
max_heap(arr,0,i-1);
}
}
vector<int> sortArray(vector<int>& arr)
{
int len = (int)arr.size();
heap_sort(arr,len);
return arr;
}
};
五、插入排序
原理: 假定前i个数据是有序的(起始一般是第一个),接着往后遍历数据,每一个数据都要找到前有序序列中的一个合适位置插入。
数据是反序时,耗费时间最长O(n²);数据是正序时,耗费时间最短O(n)
是一种稳定的排序算法
/* 力扣代码 */
class Solution{
public:
vector<int> sortArray(vector<int>& arr)
{
int len = arr.size();
int i=0;
for(int p=1;p<len;p++)
{
i = p - 1;
int temp = arr[p];
while(i>=0 && arr[i] > temp)
{
arr[i+1] = arr[i];
i--;
}
arr[i+1] = temp;
}
return arr;
}
};
六、希尔排序
原理: 一般可使用3个增量进行排序,分别是5、3、1,也就是在一组数据里面以5为间隔大小,分别划分出不同的序列分别进行插入排序,再以3为间隔大小,分别划分出不同的序列进行插入排序,最后1就是一次全序列进行插入排序
复杂度下界为O(n log²n),在中等规模的数据中表现良好。
平均时间复杂度为O(n^3/2),是一种不稳定的排序算法
/* 力扣代码 */
class Solution{
public:
void sortShell(vector<int>&arr,int len)
{
int i,j,key;
for(int inc=len/2;inc>0;inc/=2)
{
for(i=inc;i<len;i++)
{
key = arr[i];
for(j=i;j>=inc && key < arr[j-inc];j-=inc)
{
arr[j] = arr[j-inc];
}
arr[j] = key;
}
}
}
vector<int>sortArray(vector<int>&arr)
{
int len = arr.size();
sortShell(arr,len);
return arr;
}
};
七、归并排序
原理: 原理如图所示,先将一个序列一直对半划分,直到不能再划分,当划分完毕就进行合并,合并的时候分别比较左右分区的第一个元素,把较小的那个放到临时数组,一直这样合并直到合并完毕。
最好情况下可将时间复杂度降至O(n)
平均时间复杂度为O(n logn)
/* 力扣代码 */
class Solution{
public:
vector<int>tmp;
/* 划分数组 */
void msort(vector<int>&nums,int left,int right)
{
if(left < right)
{
int mid = left+(left+right)/2;
//划分左边区域
msort(nums,left,mid);
//划分右边区域
msort(nums,mid+1,right);
//合并
int i = left,j = mid+1;
int cnt = 0;
/* 合并数组/ */
while(i<=mid && j<=right)
{
if(nums[i] <= nums[j])
{
tmp[cnt++] = nums[i++];
}
else
{
tmp[cnt++] = nums[j++];
}
}
/* 合并左分区剩余序列 */
while(i <= mid)
{
tmp[cnt++] = nums[i++];
}
/* 合并右分区剩余序列 */
while(j <= right)
{
tmp[cnt++] = nums[j++];
}
/* 将排好的数组再放到原数组 */
for(int i=0;i<(right-left+1);i++)
{
nums[i+left] = tmp[i];
}
}
else
return;
}
vector<int>sortArray(vector<int>&nums)
{
int len = nums.size();
tmp.resize((int)nums.size(), 0);
msort(nums,0,len-1);
return nums;
}
};