数据结构 排序

排序

内部排序:在内存中进行排序

插入排序

直接插入排序

将序列分为两半,前一半已经有序,后一半等待排序,将i存到0上,与后面元素依次比较

void InsSort(RecordType r[], int length) {
    int i, j;

    for (i = 2; i <= length; ++i) {
        r[0] = r[i];//因为0不用,把i上元素存到0上
        for (j = i - 1; r[0].key < r[j].key; --j)//因为用的小于号,因此算法是稳定排序
            r[j + 1] = r[j];
        r[j + 1] = r[0];//j+1位置上为空位置
    }
}
//时间复杂度O(n^2)
//空间复杂度O(1)
//稳定排序

因为前面是有序排列,因此可以用折半查找法找到位置

折半插入
void BinSort(RecordType r[], int length) {
    int i, j;
    int low, high, mid;

    for (i = 2; i <= length; ++i) {
        r[0] = r[i];
        low = 1;
        high = i - 1;
        while (low <= high) {
            mid = (low + high) / 2;
            if (r[0].key < r[mid].key)
                high = mid - 1;
            else
                low = mid + 1;
        }
        for (j = i - 1; j >= low; --j)
            r[j + 1] = r[j];
        r[low] = r[0];
    }
}
shell排序
  1. 选取stride,即每间
  2. 隔s个数取为一组(可能两个以上在同一组里),在组内进行插入排序
  3. 每一轮缩小stride,取为一半
static void ShellInsert(RecordType r[], int length, int delta) {
    int i, j;

    for (i = 1 + delta; i <= length; ++i) // 1+delta为第一个子序列的第二个元素的下标
        if (r[i].key < r[i - delta].key) {
            r[0] = r[i];//i号元素存到0号上
            for (j = i - delta; j > 0 && r[0].key < r[j].key; j -= delta)//将组内前面的所有元素向后移位置
                r[j + delta] = r[j];
            r[j + delta] = r[0];
        }
}

void ShellSort(RecordType r[], int length) {
    for (int delta = length / 2; delta >= 1; delta /= 2)//递减delta
        ShellInsert(r, length, delta);
}

交换排序

冒泡排序

用swapped标记减少循环轮数

void BubbleSort(RecordType r[], int length) {
    int i, j;
    RecordType x;
    bool swapped = true;

    for (i = 1; i <= length - 1 && swapped; ++i) {
        swapped = false;
        for (j = 1; j <= length - i; ++j)
            if (r[j + 1].key < r[j].key) {
                x = r[j];
                r[j] = r[j + 1];
                r[j + 1] = x;
                swapped = true;
            }
    }
}
快速排序(插空法)

三值取中法求得参考值(pivot)

  1. 通过pivot函数取得p

  2. 取得pivot作a[0](在一维数组中取出来)

  3. 从左向右找到一个比p小的值置为low,插到空位(第一次是插到[0],后面都是pivot的位置),递增i

  4. 从右向左找到一个比p大的值为high,插到空位,递减j

  5. 若low和high相遇,将p插到空位,完成排序

  6. 快排最好的情况:p每轮都在中间位置

    最坏的情况:数列已经有序,退化为冒泡排序,p每轮都在第一个位置

static int Pivot(RecordType r[], int low, int high) {
    RecordType x = r[low];

    while (low < high) {
        while (low < high && r[high].key >= x.key)
            --high;
        if (low < high) {
            r[low] = r[high];
            ++low;
        }

        while (low < high && r[low].key < x.key)
            ++low;
        if (low < high) {
            r[high] = r[low];
            --high;
        }
    }

    r[low] = x;
    return low;
}
//pivot函数将数组分为两部分,左边全部小于p,右边全部大于p,并且返回low值作为pivot

static void _QuickSort_(RecordType r[], int low, int high) {
    if (low < high) {
        int pivot = Pivot(r, low, high);
        _QuickSort_(r, low, pivot - 1);
        _QuickSort_(r, pivot + 1, high);
    }
}

// 科里化,wrapper,可以在调用函数的时候减少传参
void QuickSort(RecordType r[], int length) {
    _QuickSort_(r, 1, length);
}
快速排序(交换法)

选择排序

  • 选择排序:在每一轮中,选择未排序部分的最小(或最大)元素,然后将其与未排序部分的第一个元素交换位置。这个过程重复进行,直到整个数组被排序。

  • 插入排序:从数组的第二个元素开始,将每个元素与前面已排序的元素进行比较,找到合适的位置插入,直到所有元素都被插入到正确的位置。

简单选择
void SelectSort(RecordType r[], int length) {
    int k, j, i;
    RecordType x;

    for (i = 1; i < length; ++i) {//外层循环遍历未排序部分
        k = i;
        for (j = i + 1; j <= length; ++j)
            if (r[j].key < r[k].key)
                k = j;//找到最小值

        if (k != i) {
            x = r[i];
            r[i] = r[k];
            r[k] = x;//完成swap
        }
    }
}

树形排序:在树中完成排序,每排一个将其置为无穷大,

堆排序
  1. 将一个原始序列(一维数组)视为一个堆,初始化为大顶堆(如何创建?)
  2. 交换最后一个堆首和队尾的元素,重构大顶堆
  3. 重复交换,直到只剩根节点

初始化堆的方法:从第一个非叶结点开始,比较左右子树,将大的值一路上溯到根结点

在这里插入图片描述

/*
 * 假设r[k..m]是以r[k]为根的完全二叉树,且分别以r[2k]和r[2k+1]为根的左、右子树为大根堆,
 * 调整r[k],使整个序列r[k..m]满足堆的性质
 */

//上滤函数(上滤为大顶堆)
static void sift(RecordType r[], int k, int m) {//m为最后一个索引
    RecordType t = r[k]; // 暂存"根"记录r[k]
    bool finished = false;

    int i = k; //i是子树的根节点序号
    int j = 2 * i; //初始时,j是i的左孩子序号
    //沿着i的孩子j,往下筛选
    while (j <= m && !finished) {
        if (j < m && r[j].key < r[j + 1].key) //j+1是i右孩子的序号,判断右子是否大于左子
            ++j; //若存在右子树,且右子树根的关键字大,则沿右分支"筛选"
        if (t.key >= r[j].key)
            finished = true; //如果父节点大,筛选完毕
        else {
            r[i] = r[j]; //子树的根节点被其值最大的那个孩子覆盖
            i = j;   //以j为子树的根,进行下一趟筛选
            j = 2 * i;
        }
    }
    //i已经不再是原来的i,而是原来的某个深度的孩子的序号
    r[i] = t; //原来的根r[k]填入到恰当的位置
}





//从无序数组中建一个堆,length为数组的长度
static void create_heap(RecordType r[], int length) {
    /*
     * 自第[n/2]个记录开始进行筛选建堆
     * 从这个记录开始往前(序号变小)都是非叶节点。当然了,此后的一定都是叶节点
     */
    for (int i = length / 2; i >= 1; --i)
        sift(r, i, length);//在所有非叶节点中完成上滤
}





//对r[1..n]进行堆排序,执行本算法后,r中记录按关键字由大到小有序排列
void HeapSort(RecordType r[], int length) {
    RecordType b;

    create_heap(r, length);

    for (int i = length; i >= 2; --i) {
        //将堆顶记录和堆中的最后一个记录互换
        b = r[1];
        r[1] = r[i];
        r[i] = b;

        //进行调整,使r[1..i-1]变成堆
        sift(r, 1, i - 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值