1. 冒泡排序
依次两两比较,如果大就交换,所以最后面会保存到较大值(或较小值),然后再进行新一轮的比较,所以比较轮数是length-1,每一轮比到第length-i-1项(因为后面的都比较完了)
public static void bubbleSort(int[] arr) {
for(int i =0;i<arr.length-1;i++) {
for(int j=0;j<arr.length-1-i;j++) {
int temp;
if(arr[j]>arr[j+1]) { //升序
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
2. 快速排序
思想:找出一个标准数,以此数为基准,将序列分为两部分,前面是小于等于这个数的数,后面是大于这个数的数,再将这两部分再做类似的操作,即可以使用递归的思想
步骤:
取第一个数为标准数
然后记录排序标志low,high
循环找出比标准数大的数和比标准数小的数,分成前面部分和后面部分
先从右边数开始
如果右边的数字比标准数大,标志前移,否则就用右边的数字替换左边的数字
替换了之后就从左边开始比
如果左边的数字比标准数小,标志后移,否则,就用左边的数替换右边的数
把标准数赋值给所在低位置的元素,注意,此时低位置和高位置重合
递归处理小的那部分数字
递归处理大的那部分数字
public static void quickSort(int[] arr, int start, int end) {
if(start<end) {
//把数组中的第0个数字作为标准数
int stard = arr[start];
//记录需要排序的下标
int low = start;
int high = end;
//循环找出比标准数大的数和比标准数小的数,分成前面部分和后面部分
while(low<high) {
//右边的数字比标准数大,标志前移
while(low<high && stard<=arr[high]) {
high--;
}
//如果右边的数字比标准数小,就用右边的数字替换左边的数字
arr[low] = arr[high];
//替换了之后就从左边开始比
//如果左边的数字比标准数小,标志后移
while(low<high && arr[low] <= stard) {
low++;
}
//否则,就用左边的数替换右边的数
arr[high] = arr[low];
}
//把标准数赋值给所在低位置的元素,注意,此时低位置和高位置重合
arr[low] = stard;
//递归处理小的那部分数字
quickSort(arr,start,low);
//递归处理大的那部分数字
quickSort(arr,low+1,end);
}
}
3. 插入排序
插入排序思想:从第二个开始,依次和前面的元素进行比较,如果当前元素比前一个小就交换,依次进行就把前面的元素变成有序的了。然后在从第三个、第四个依次下去
步骤:
从第二个元素开始遍历所有元素
如果当前元素比前一个数字小,就把当前元素保存到临时变量中
再去遍历当前数字前面的所有数字,所以j从i-1开始
如果j>=0 && temp<arr[j],把前一个数字赋给后一个数字
两种情况结束循环:
1.j=-1
2.当前面的数字已经比temp小时,就不会把前一个数字赋给后一个数字,而是把temp赋给后一个数字
void dselect(vector<int>& input) {
int len = input.size();
for (int i = 1; i < len; i++) {
int temp = input[i];
if (input[i] < input[i - 1]) {
int j = i - 1;
for (; j >= 0 && temp < input[j]; j--) {
input[j + 1] = input[j];
}
input[j + 1] = temp;
}
}
}
4. 希尔排序
希尔排序思想:引入步长,length/2,将序列依据步长分组,将每一组依次采用直接插入排序
采用步长思想可以快速将大的值移到后面,将小的值移到前面。
void xierSort(vector<int>& input){
int len = input.size();
for(int d=len/2;d>0;d/=2){ //遍历所有的步长
for(int i=d;i<len;i++){ //从中间开始遍历所有组的元素
for(int j=i-d;j>=0;j-=d){ //遍历本组的所有元素
if(input[j+d]<input[j]){ //如果当前元素大于加上步长的那个元素,就交换
int temp = input[j];
input[j]=input[j+d];
input[j+d]=temp;
}
}
}
}
}
5. 选择排序:
选择排序思想:
先假设第一个为最小值,将于后面所有元素比较,如果有比它更小的数,就更新最小值下标,然后再把它交换,放到最前面,然后标志位++,把第二个当成最小值,将它和后面的比较。。。。
这样,最前面的都是依次从小到大好了的了。
public static void selectSort(int[] arr) {
//遍历所有的元素
for(int i=0;i<arr.length;i++) {
//每一轮的标志位,默认第一个元素时最小值
int minIndex = i;
//把当前定义的的最小数和后面的数依次比较,并记录较小数的下标
for(int j=i+1;j<arr.length;j++) {
if(arr[minIndex]>arr[j]) {
minIndex = j;
}
}
//如果找到的minIndex还是原来的,即等于i,就不用交换
//如果不是,就交换,把找到的最小值放到最前面
if(i!=minIndex) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
6. 归并排序
归并排序思想:
归并方法可以将两个有序序列排序,所以要将一个序列排序,就不断将这个序列分成两部分,直到每部分只有一个元素,因为一个元素的序列可以认为是有序数列,在进行归并,实现排序,不断递归就可以实现。
//还是用数组来实现,先把vector存到数组里面进行
class Solution {
public:
void merge(int a[],int* left,int leftlen,int* right,int rightlen){
int i=0,j=0,k=0;
while(i<leftlen && j<rightlen){
if(left[i]<right[j]){
a[k++]=left[i++];
}else{
a[k++]=right[j++];
}
}
while(i<leftlen){
a[k++]=left[i++];
}
while(j<rightlen){
a[k++]=right[j++];
}
}
void mergeSort(int a[],int n){
int mid = n/2;
if(n<2) return;
int* left = new int[mid];
int* right = new int[n-mid];
for(int i=0;i<mid;i++){
left[i]=a[i];
}
for(int i=mid;i<n;i++){
right[i-mid]=a[i];
}
mergeSort(left,mid);
mergeSort(right,n-mid);
merge(a,left,mid,right,n-mid);
delete[] left;
delete[] right;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len = input.size();
vector<int> array;
if (len == 0 || k == 0 || k > len) return array;
int* a = new int[len];
for(int i=0;i<len;i++){
a[i]=input[i];
}
mergeSort(a,len);
for (int i = 0; i < k; i++) {
array.push_back(a[i]);
}
return array;
}
};
//用vector实现的归并算法
void MergeSortHelp(vector<int>& data, vector<int>& copy, int start, int end) {
if (start == end)
return;
int mid = (start + end) / 2;
MergeSortHelp(copy, data, start, mid);
//不太懂这里的操作,每一次归并排序后的值又到了data中保存
//下面都还是再copy中,这个时候copy得到的是上一次归并后的值,即上一次data的值
//不太明白这个操作,好像指针一样。
MergeSortHelp(copy, data, mid + 1, end);
int i = start, j = mid + 1;
int index = start;
while (i <= mid && j <= end) {
if (data[i] < data[j])
copy[index++] = data[i++]; //从小到大
else
copy[index++] = data[j++];//从小到大
}
while (i <= mid)
copy[index++] = data[i++];
while (j <= end)
copy[index++] = data[j++];
}
vector<int> MergeSort(vector<int>& v) {
bool Invalid_Input = false;
vector<int>copy;
int len = v.size();
if (len <= 0) {
Invalid_Input = false;
return copy;
}
for (int i = 0; i < len; i++)
copy.push_back(v[i]);
MergeSortHelp(v, copy, 0, len - 1);
return copy;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len = input.size();
vector<int> array;
if (len == 0 || k == 0 || k > len) return array;
int start = 0;
int end = len - 1;
vector<int> res;
res = MergeSort(input);
for (int i = 0; i < k; i++) {
array.push_back(res[i]);
}
return array;
}
int main() {
vector<int> invec{ 1, 2, 3, 7, 5, 9, 0, 11, 15, 8, 30, 20, 19 };
//vector<int> *p = new int[]{1, 2, 3, 7, 5, 9, 0, 11, 15, 8, 30, 20, 19};
vector<int> res;
res=GetLeastNumbers_Solution(invec, 4);
for (auto& i : res) {
cout << i << " ";
}
cout << endl;
}
7. 基数排序
基数排序思想:适合有很大的数字,也有很小的数字
{512,2,45,67,897,56,89,112,33,156,342}
根据个位、十位、百位上的数来排序,创建10个桶0~9,因为每个位上的数字必是其中之一,
桶可以用一个二维数组、队列实现
第一轮,先从个位,将数组中的数放入个位数对应的桶中,再依顺序存回arr
第二轮,再从十位,将数组中的数放入个位数对应的桶中,再依顺序存回arr
。。。。。
class Solution {
public:
void radixSort(vector<int>& input){
int len = input.size();
int max=0;
for(int i=0;i<len;i++){
if(input[i]>max){
max=input[i];
}
}
int maxlen=1;
while(max/10){
maxlen++;
max/=10;
}
queue<int> que[10];
for(int i=0,n=1;i<maxlen;i++,n*=10){
for(int j=0;j<len;j++){
int tonIndex=input[j]/n%10;
que[tonIndex].push(input[j]);
}
int inputIndex=0;
for(int k=0;k<10;k++){
while(!que[k].empty()){
input[inputIndex]=que[k].front();
que[k].pop();
inputIndex++;
}
}
}
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len = input.size();
vector<int> array;
if (len == 0 || k == 0 || k > len) return array;
radixSort(input);
for (int i = 0; i < k; i++) {
array.push_back(input[i]);
}
return array;
}
};
8. 堆排序
大顶堆:父节点的值大于子节点的值,升序排列
把一个顺序存储的二叉树改成一个大顶堆,然后把根节点的权和最后一个叶子节点的权值交换,然后把最后叶子节点的值取出存放,这样就取出一个最大值
然后再把新的堆变成一个大顶堆
public static void heapSort(int[] arr) {
//开始位置为最后一个非叶子节点,即最后一个节点的父节点
//int start = (arr.length-1)/2;
int start = arr.length/2-1;
//从最后一个非叶子节点开始,依次往前遍历非叶子节点,
//将非叶子节点与其两个子节点比较,调整为大顶堆
for(int i=start;i>=0;i--) {
maxHeap(arr,arr.length,i);
}
//堆排序完了
//先把数组中的第0个元素和第一个交换,这样就是第0个元素比下面的子节点大,
//所以直接从0节点开始,把新的堆变为为大顶堆
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 static void maxHeap(int[] arr, int size, int index) {
//左子节点
int leftIndex =2*index+1;
//右子节点
int rightIndex =2*index+2;
int max = index;
//和两个子节点比较,a找出最大的节点
if(leftIndex<size && arr[leftIndex]>arr[max]) {
max = leftIndex;
}
if(rightIndex<size && arr[rightIndex]>arr[max]) {
max = rightIndex;
}
//交换位置
if(max!=index) {
int temp = arr[index];
arr[index] = arr[max];
arr[max] = temp;
//交换之后可能会破坏之前拍好的堆,所以,之前拍好的堆要重新调整
maxHeap(arr,size,max);
}
}
堆排序查找最大的k的数
class Solution {
public:
void radixSort(vector<int>& input){
int len = input.size();
int max=0;
for(int i=0;i<len;i++){
if(input[i]>max){
max=input[i];
}
}
int maxlen=1;
while(max/10){
maxlen++;
max/=10;
}
queue<int> que[10];
for(int i=0,n=1;i<maxlen;i++,n*=10){
for(int j=0;j<len;j++){
int tonIndex=input[j]/n%10;
que[tonIndex].push(input[j]);
}
int inputIndex=0;
for(int k=0;k<10;k++){
while(!que[k].empty()){
input[inputIndex]=que[k].front();
que[k].pop();
inputIndex++;
}
}
}
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len = input.size();
vector<int> array;
if (len == 0 || k == 0 || k > len) return array;
radixSort(input);
for (int i = 0; i < k; i++) {
array.push_back(input[i]);
}
return array;
}
};
复杂度比较
稳定性分析:
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法
详细分析请参考此博文