数据结构-排序

数据结构-排序

image-20231222092424724image-20231222092545813image-20231222092531435

直接插入

image-20231221233828975

image-20231222092636312

折半插入

image-20231222093220852image-20231222093227413

image-20231222093244242

希尔排序

image-20231222093435088

image-20231222093948294

#include <iostream>
using namespace std;

// 希尔排序函数
void ShellSort(int *arr, int len) {
	int grp = len / 2;		// 计算增量

	for (; grp > 0; grp = grp / 2) {	// 每次计算完一轮,增量再除以二

		// 选择排序
		for (int i = grp; i < len; i++) {	
			int cur = arr[i];	// 先空出第二个数的位置
			int j = 0;

			// 根据增量往前比较,小的在前,大的在后
			for (j = i - grp; j >= 0 && arr[j] > cur; j = j - grp) {
				arr[j + grp] = arr[j];	// 符合条件将前面的数据移动到空出的位置
			}
			arr[j + grp] = cur;	// 最后将待插入数据cur插入到前面空出的位置
		}
	}
	
}


WechatIMG1477

image-20231222101224184

image-20231222101236716

起泡排序(加强)

在每一次起泡的过程中加入一次标志,查看有没有交换,如果没有交换就表示全部正确

image-20231222101613730image-20231222101731536

快速排序

image-20231222102145048

image-20231222102322630

// 分区函数
//遍历一遍,然后把小于的按顺序存放;最后把我的中间元素放在下一位
int partition(int* arr, int low, int high, int pivot) {
  // 将枢轴元素放在数组的中间
  int pivotIndex = (low + high) / 2;
  swap(arr[pivotIndex], arr[high]);

  // 将数组分为两部分,一部分比枢轴小,另一部分比枢轴大
  int partitionIndex = low;
  for (int i = low; i < high; i++) {
    if (arr[i] < pivot) {
      swap(arr[i], arr[partitionIndex]);
      partitionIndex++;
    }
  }

  // 将枢轴元素放在正确的位置
  swap(arr[partitionIndex], arr[high]);

  // 返回枢轴元素的索引
  return partitionIndex;
}

// 快速排序算法
void quickSort(int* arr, int low, int high) {
  // 递归基线条件
  if (low >= high) {
    return;
  }
  // 选择一个枢轴元素
  int pivot = arr[high];

  // 将数组分为两部分,一部分比枢轴小,另一部分比枢轴大
  int partitionIndex = partition(arr, low, high, pivot);

  // 对两部分递归调用快速排序
  quickSort(arr, low, partitionIndex - 1);
  quickSort(arr, partitionIndex + 1, high);
}



// 交换两个元素
void swap(int* a, int* b) {
  int temp = *a;
  *a = *b;
  *b = temp;
}

image-20231222153317753

image-20231222153342559

选择排序

image-20231222155232103

image-20231222160336082

image-20231222160349073

最大堆

image-20231222160545191

归并排序

image-20231222160951928

image-20231222204925289

image-20231222205017500image-20231222205027075

image-20231222205126410

image-20231222210546326

image-20231222210722797

image-20231222210730019

image-20231222210740783

image-20231222210748218

image-20231222210755209

image-20231222210801650

  • 一个排列中所有逆序总数叫做这个排列的逆序数。
  • 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。

作业

image-20231222210836072

当一个比较大的元素位于更大的元素右侧,且二者在偏离自己位置的时候;这个元素就会朝相反方向移动
比如 9 8 1 2->8 9 1 2->8 1 9 2->8 1 2 9,在这轮起泡中,8被向左移动了,相对于其位置[2],是相反的

在快速排序中,由于进行分块,比选定元素小的会被进行按顺序插入新的序列之中,不会进行两两元素分别比较,因此一般不会,但是局部也会出现这种情况

1 2 3 7 5 4 6,选定6作为枢纽
1 2 3 5 4 6 7此时5本来在自己应该的位置,但是被左移了一次

image-20231222212633931

交换对于B之后的元素没有影响,对于A之前的元素没有影响,仅仅对于介于AB之间的元素来说,减少的逆序 = 比A小的元素个数 + 比B大的元素个数 - 比A大的元素个数 - 比B小的元素的元素个数 + AB本身是否是一对逆序所导致的变化

考虑极限情况,AB在首尾,并且AB分别为最大和最小,减少的逆序为 2 ∗ ( n − 2 ) + 1 2*(n-2)+1 2n2+1

2 n − 3 2n-3 2n3

image-20231222214345522

使用栈进行非递归快速排序的简要过程:

  1. 将整个数组的初始范围(通常是整个数组)入栈,表示待排序区间。

  2. 循环执行以下步骤,直到栈为空:

    ​ a. 弹出栈顶的待排序区间,记为[left, right]。

    ​ b. 对该区间进行快速排序的一次划分,得到枢纽元素的位置pivot。

    ​ c. 如果划分后的左右子数组非空,则将左右子数组的范围分别入栈。

从中我们不难看出,每一次的结果对于别的区域没有什么影响,也就是先入先出或者先入后出没有什么区别

  1. 将整个数组的初始范围(通常是整个数组)入队,表示待排序区间。

  2. 循环执行以下步骤,直到栈为空:

    ​ a.将栈顶的待排序区间出队,记为[left, right]。

    ​ b. 对该区间进行快速排序的一次划分,得到枢纽元素的位置pivot。

    ​ c. 如果划分后的左右子数组非空,则将左右子数组的范围分别入队。

image-20231222215049877

void cocktailSort(std::vector<int> &arr){
    int high = 0;
    bool switched = false;
    for(int i = 0;i < arr.size();i++){
        high = 0;
        switch(i %2){
            case(0):
            //偶数代表从左到右
            for(int j = i/2;j < arr.size() -1;j++){
                if(arr[j] > arr[j+1]){
                    int temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                    high = j+1;
                }
            }
            break;

            case(1):
            //基数代表从右到左
            for(int j = arr.size()-1 - i/2;j > 0;j--){
                if(arr[j] < arr[j-1]){
                    int temp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j -1]  = temp;
                    high = j;
                }
            }
            break;            
        }
        //没有交换表示成功
        if(0 == high){
            break;
        }
    }
}
image-20231222224247355

image-20231222225422930

(1) 结束的条件是当一趟奇数扫描和一趟偶数扫描都没有发生交换时,表明序列已经排好序,排序过程结束。

(2) 奇偶交换排序的算法如下:

void oddEvenSort(int arr[], int n) {
    bool sorted = false; 
    while (!sorted) {
        sorted = true; 
        // 奇数项扫描
        for (int i = 1; i < n - 1; i += 2) {
            if (arr[i] > arr[i + 1]) {
                swap(arr[i], arr[i + 1]);
                sorted = false; 
            }
        }
        // 偶数项扫描
        for (int i = 0; i < n - 1; i += 2) {
            if (arr[i] > arr[i + 1]) {
                swap(arr[i], arr[i + 1]);
                sorted = false; 
            }
        }
    }
}

(3) 当待排序序列的初始排列是从小到大有序时,奇偶排序过程中的排序码比较次数是n-1

这是因为在每一趟奇数扫描和偶数扫描中,每一对相邻的元素都需要比较和可能的交换。在最坏情况下,每一对元素都需要交换,导致总的比较次数为O( ( n − 1 ) 2 (n-1)^2 (n1)2)。

image-20231222225439072

// 交换数组中两个元素的位置
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

// 分割函数,选取一个枢纽元素将数组分成两部分
int partition(int arr[], int low, int high) {
    int pivot = arr[high];
    int pos = low;
    for(int i = low;i < high;i++){
        if(arr[i] <= pivot){
            swap(arr[i],arr[pos]);
            pos++;
        }
    }
    swap(arr[high],arr[pos]);
    return pos;
}

void quickSort(int a[],int low,int high){
    queue<pair<int,int>> q;
    q.push({low,high});
    while(!q.empty()){
        pair<int,int> current = q.front();
        q.pop();

        int low_1 = current.first;
        int high_1 = current.second;

        if(low_1 < high_1){
            int pivot = partition(a,low_1,high_1);

            q.push({low_1,pivot -1});
            q.push({pivot+1,high_1});
        }
    }
}
image-20231222232817675
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值