排序代码总结

8大排序算法的总结

  • 常见算法思想;
  • 简介
    • 插入排序
    • 冒泡排序
    • 直接插入排序
    • 折半插入排序
    • 归并排序
    • 快速排序
    • 希尔排序
    • 堆排序
    • 直接选择排序
  • 稳定性比较
    • 快速排序、希尔排序、堆排序、直接选择排序不是稳定的排序算法;
    • 基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法;
    • 稳定排序的意思是: 假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的;
      • 简单理解就是保持了相同元素在原来序列中的先后关系;
  • 快速排序的代码:
#include "../leetcodeutil.h"

int patition(vector<int>& nums, int l, int r) {
    int pivot = nums[r];        // 以最右边为轴
    int i = l;
    for(int j = i; j < r; ++j) {
        if(nums[j] < pivot) {
            swap(nums[i++], nums[j]);
        }
    }
    swap(nums[i], nums[r]);

    return i;   // 返回最后的轴点
}
// int patition(vector<int>& nums, int l, int r) {
//     int pivot = nums[l];   // 以最左边为轴
//     int i = r;
//     for(int j = i; j >= l; --j) {
//         if(nums[j] > pivot) {
//             swap(nums[i--], nums[j]);
//         }
//     }
//     swap(nums[i], nums[l]);

//     return i;   // 返回最后的轴点
// }

void quicksortutil(vector<int> &nums, int l, int r) {
    if(l < r) {
        int pivot = patition(nums, l, r);
        quicksortutil(nums, l, pivot - 1);
        quicksortutil(nums, pivot + 1, r);
    }
}

void quicksort(vector<int> &nums) {
    quicksortutil(nums, 0, nums.size() - 1);
}

int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    quicksort(iv);
    cout << iv << endl;

    return 0;
}
  • 冒泡排序源码
#include "../leetcodeutil.h"

void bubblesort(vector<int> &nums) {
    int len = nums.size();
    if(len == 0) return;
    for(int i = 0; i < len; ++i) {
        for(int j = len - 1; j > i; --j) { // 正宗的冒泡排序应该是从后往前排
            if(nums[j] < nums[j-1]) {      // 从后往前找, 找出小的交换
                swap(nums[j], nums[j-1]);
            }
        }
    }
}
// 优化的冒泡排序, 可以去掉本身有序的情况
void bubblesort2(vector<int> &nums) {
    int len = nums.size();
    if(len == 0) return;            // 时间复杂度还是O(n2)的
    bool isUnordered = true;        // 减枝优化, 如果本身就是有序, 直接返回
    for(int i = 0; i < len && isUnordered; ++i) {
        isUnordered = false;
        for(int j = len - 1; j > i; --j) {
            if(nums[j] < nums[j-1]) {      //从后往前找, 找出小的交换
                swap(nums[j], nums[j-1]);
                isUnordered = true; // 进行了一次交换, 证明之前是无序的;
            }
        }
    }
}

int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    // bubblesort(iv);
    bubblesort2(iv);
    cout << iv << endl;

    return 0;
}
  • 希尔排序源码
#include "../leetcodeutil.h"

void shellsort(vector<int> &nums) {
    // 希尔排序就是有增量的直接插入排序,所以将原先直接插入代码修改一下,把步进长度改为增量即可
    int len = nums.size();
    int i, j;
    int increment = len;
    int key;
    while(increment > 1) {
        increment = increment/3 + 1;     // 增量序列
        for(i = increment; i < len; i++) {
            key = nums[i];
            j = i - increment;
            while(j >= 0) {
                if(key < nums[j]) {
                    int tmp = nums[j];
                    nums[j] = key;
                    nums[j+increment] = tmp;
                }
            }
            j = j - increment;
        }
    }
}


int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    shellsort(iv);
    cout << iv << endl;

    return 0;
}
  • 选择排序的源码
#include "../leetcodeutil.h"
// 大概的思路就是找到最小的元素, 并与之交换
void selectionsort(vector<int> &nums) {
    int len = nums.size();
    if(len == 0) return;
    for(int i = 0; i < len; ++i) {
        int min = i;
        for(int j = i+1; j < len; j++) {
            if(nums[j] < nums[min]) {
                min = j;
            }
        }
        swap(nums[i], nums[min]);
    }
}

int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    selectionsort(iv);
    cout << iv << endl;

    return 0;
}
  • 插入排序的源码
#include "../leetcodeutil.h"
// 插入排序的的思想是找到相应的位置插入进去
void insertionsort(vector<int> &nums) {
    int len = nums.size();
    if(len == 0) return;
    for(int i = 1; i < len; ++i) {
        int val = nums[i];
        int j = i - 1;
        while(j >= 0 && nums[j] > val) {
            nums[j+1] = nums[j];
            --j;
        }
        nums[j+1] = val;
    }
}

int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    insertionsort(iv);
    cout << iv << endl;

    return 0;
}

  • 归并排序的源码
#include "../leetcodeutil.h"

using namespace std;

void merge(vector<int>& nums, int l, int m, int r) {
    int n1 = m - l + 1; // 第一部分数组的长度
    int n2 = r - m;     // 第二部分数组的长度
    int L[n1], R[n2];   // 将数组元素复制到临时数组
    for(int i = 0; i < n1; ++i) {
        L[i] = nums[l+i];
    }
    for(int i = 0; i < n2; ++i) {
        R[i] = nums[m+i+1];
    }
    // vector<int> L(nums.begin() + l, nums.begin() + m + l);
    // vector<int> R(nums.begin()+m+l, nums.begin()+r+1);
    // 将两个临时数组归并到nums[l,···,r]
    int i = 0, j = 0, k = l;
    while(i < n1 && j < n2) {
         nums[k++] = L[i] < R[j] ? L[i++] :   // 那个小就放前面
                                   R[j++];
    }
    // 如果有一个数组有剩余的部分就完全复制拷贝就OK
    // 拷贝L[]剩下的元素
    while(i < n1) {
        nums[k++] = L[i++];
    }
    // 拷贝R[]剩下的元素
    while(j < n2) {
        nums[k++] = R[j++];
    }
}

void mergesortutil(vector<int>& nums, int l, int r) {
    if(l < r) {
        int m = l + (r - l) / 2;    //防止溢出
        mergesortutil(nums, l, m);
        mergesortutil(nums, m+1, r);
        merge(nums, l, m, r);
    }
}

void mergesort(vector<int> &nums) {
    if(nums.size() == 0) 
        return;
    mergesortutil(nums, 0, nums.size()-1);
}

int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    mergesort(iv);
    cout << iv << endl;

    return 0;
}
  • 堆排序的源码
#include "../leetcodeutil.h"

using namespace std;
// 堆排序的关键是在于建堆(堆本质上可以看做是一个二叉树, 根节点为最大或最小值)
// 调整以root为根节点的子树, 使其符合最大堆, 其中n为堆结构的节点数
void heapAdjust(vector<int> &nums, int n, int root) {
    int max_ = root;
    int L = 2*root + 1;     // 左孩子
    int R = 2*root + 2;     // 右孩子
    // 左孩子大于根
    if(L < n && nums[L] > nums[max_]) {
        max_ = L;
    }
    if(R < n && nums[R] > nums[max_]) {
        max_ = R;
    }
    // 最大的元素不是根
    if(max_ != root) {
        swap(nums[root], nums[max_]);
        heapAdjust(nums, n, max_);  // 递归调整以max_为根的子树
    }
}

void heapsort(vector<int> &nums) {
    int len = nums.size();
    // 创建最大堆, 重新排列数组
    for(int i = len/2 - 1; i >= 0; --i) {
        heapAdjust(nums, len, i);
    }
    // 堆排序将依次把根元素移动到数组尾部, 并进行调整
    for(int i = len-1; i >= 0; --i) {
        swap(nums[0], nums[i]); // 第一个元素始终是根元素, 把根元素放到数组的末尾
        heapAdjust(nums, i, 0); // 注意堆中的元素是逐个减少的
    }
}

int main(int argc, char const *argv[])
{
    vector<int> iv{1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    heapsort(iv);
    cout << iv << endl;

    return 0;
}
  • leetcodeutil.h头文件是刷题自己写的接口
#ifndef _LEET_CODE_UTIL_H_
#define _LEET_CODE_UTIL_H_
#include <bits/stdc++.h>
#include <ctime>
using namespace std;
// 链表结点
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
// 二叉树结点
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 打印vector中的所有元素
template <typename T>
ostream& operator<<(ostream& out, const vector<T>& v)
{
    for (int i = 0; i < v.size(); ++i) {
        i == 0 ? out << v[i] : cout << ' ' << v[i];
    }
    return out;
}
// 打印二维vector中的所有元素
template <typename T>
ostream& operator<<(ostream& out, const vector<vector<T>>& v) {
    for (int i = 0; i < v.size(); ++i) {
        i == 0 ? out << v[i] : out << '\n' << v[i];
    }
    return out;
}
// 以initializer_list来初始化链表
template <typename T>
ListNode* createList(const initializer_list<T>& v) {
    ListNode dummy(0);
    ListNode* prev = &dummy;
    for (const auto& e : v) {
        prev->next = new ListNode(e);
        prev = prev->next;
    }
    return dummy.next;
}
// 销毁链表
void destroyList(ListNode* &head) {
    ListNode* curr = head, *next;
    while (curr) {
        next = curr->next;
        delete curr;
        curr = next;
    }
    head = NULL;
}
// 打印链表元素
ostream& operator<<(ostream& out, const ListNode* list) {
    while (list != NULL) {
        list->next == NULL ? out << list->val : out << list->val << ' ';
        list = list->next;
    }
    return out;
}
// 前序遍历二叉树
void preorderTraverseTree(TreeNode *root) {
    if (root == NULL) return;
    cout << root->val << ' ';
    preorderTraverseTree(root->left);
    preorderTraverseTree(root->right);
}
// 中序遍历二叉树
void inorderTraverseTree(TreeNode *root) {
    if (root == NULL) return;
    inorderTraverseTree(root->left);
    cout << root->val << ' ';
    inorderTraverseTree(root->right);
}
// 后序遍历二叉树
void postorderTraverseTree(TreeNode *root) {
    if (root == NULL) return;
    postorderTraverseTree(root->left);
    postorderTraverseTree(root->right);
    cout << root->val << ' ';
}
// 层序遍历二叉树
void levelorderTraverseTree(TreeNode *root) {
    if (root == NULL) return;
    queue<TreeNode *> q;
    TreeNode *curr;
    q.push(root);
    while (!q.empty()) {
        curr = q.front();
        cout << curr->val << ' ';
        if (curr->left)  q.push(curr->left);
        if (curr->right) q.push(curr->right);
        q.pop();
    }
}
// 根据层序遍历的 vector 构造二叉树递归函数
static TreeNode* createTree(const vector<int>& nums, int n) {
    if (nums[n] == '#') return NULL;
    TreeNode *node = new TreeNode(nums[n]);
    int len = nums.size();
    int left  = n*2 + 1;
    int right = n*2 + 2;
    node->left  = left  < len ? createTree(nums, left)  : NULL;
    node->right = right < len ? createTree(nums, right) : NULL;
    return node;
}
// 根据层序遍历的 initializer_list 构造二叉树
template <typename T>
TreeNode* createTree(const initializer_list<T>& nums) {
    vector<T> v;
    for (auto it = nums.begin(); it != nums.end(); ++it) {
        v.push_back(*it);
    }
    return createTree(v, 0);
}
// 随机数辅助类
class MathUtil {
public:
    // 产生[a, b)之间的随机数
    static int random(int a, int b) {
        assert(b > a);
        return a + rand() % (b-a);
    }
};
struct SrandInitalizer {
    SrandInitalizer() { srand(time(NULL)); }
};
SrandInitalizer _srandinit;
// 时间辅助类
class TimeUtil {
public:
  // 返回当前进程运行的时间 (单位: ms)
  static int getCurrentTimeMs() { return (int)clock() / (CLOCKS_PER_SEC / 1000); }
  // 返回当前进程运行的时间 (单位: us)
  static int getCurrentTimeUs() { return (int)clock() / (CLOCKS_PER_SEC / 1000000); }
};

#endif // _LEET_CODE_UTIL_H_

posted on 2018-09-25 17:26 coding-for-self 阅读() 评论() 编辑 收藏

版权声明:本文为longjiang-uestc原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/longjiang-uestc/p/9700702.html