跳转至

数据结构与算法笔记

二叉树

二叉树结构

C++
struct TreeNode
{
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};

N叉树结构

C++
class Node
{
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val)
    {
        val = _val;
    }

    Node(int _val, vector<Node*> _children)
    {
        val = _val;
        children = _children;
    }
};

翻转

C++
void Reverse(vector<int>& result)
{
    for (int i = 0, j = result.size() - 1; i < j; ++i, --j)
    {
        int temp = result[i];
        result[i] = result[j];
        result[j] = temp;
    }
}

二叉树遍历

递归

前序遍历
C++
void traversal(TreeNode* root, vector<int>& result)
{
    if (root == nullptr) return;
    result.push_back(root->val);
    traversal(root->left, result);
    traversal(root->right, result);
}
vector<int> preorderTraversal(TreeNode *root)
{
    vector<int> result;
    traversal(root, result);
    return result;
}
后序遍历
C++
void traversal(TreeNode* root, vector<int>& result)
{
    if (root == nullptr) return;
    traversal(root->left, result);
    traversal(root->right, result);
    result.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root)
{
    vector<int> result;
    traversal(root, result);
    return result;
}
中序遍历
C++
void traversal(TreeNode* root, vector<int>& result)
{
    if (root == nullptr) return;
    traversal(root->left, result);
    result.push_back(root->val);
    traversal(root->right, result);
}
vector<int> inorderTraversal(TreeNode* root)
{
    vector<int> result;
    traversal(root, result);
    return result;
}

迭代

前序遍历(方法一)
C++
vector<int> preorderTraversal(TreeNode* root) 
{
    vector<int> result;
    if(root==nullptr) return result;
    stack<TreeNode*> st_TreeNode;
    st_TreeNode.push(root);
    while(!st_TreeNode.empty())
    {
        TreeNode* node=st_TreeNode.top();
        st_TreeNode.pop();
        result.push_back(node->val);
        if(node->right!=nullptr) st_TreeNode.push(node->right);
        if(node->left!=nullptr) st_TreeNode.push(node->left);   
    }
    return result;
}
前序遍历(方法二)
C++
vector<int> preorderTraversal(TreeNode* root)
{
    vector<int> result;
    if (root == nullptr)
        return result;
    stack<TreeNode*> st_TreeNode;
    TreeNode* node = root;
    while (node != nullptr || !st_TreeNode.empty())
    {
        while (node != nullptr)
        {
            st_TreeNode.push(node);
            result.push_back(node->val);
            node = node->left;
        }
        if (!st_TreeNode.empty())
        {
            node = st_TreeNode.top();
            st_TreeNode.pop();
            node = node->right;
        }
    }
    return result;
}
后序遍历(方法一)
C++
vector<int> postorderTraversal(TreeNode* root)
{
    vector<int> result;
    if (root == nullptr)
        return result;
    stack<TreeNode*> st_TreeNode;
    st_TreeNode.push(root);
    while (!st_TreeNode.empty())
    {
        TreeNode* node = st_TreeNode.top();
        st_TreeNode.pop();
        result.push_back(node->val);
        if (node->left != nullptr)
            st_TreeNode.push(node->left);
        if (node->right != nullptr)
            st_TreeNode.push(node->right);
    }
    Reverse(result);
    return result;
}
后序遍历(方法二)
C++
vector<int> postorderTraversal(TreeNode* root)
{
    vector<int> result;
    if (root == nullptr)
        return result;
    stack<TreeNode*> st_TreeNode;
    TreeNode* node = root;
    while (node != nullptr || !st_TreeNode.empty())
    {
        while (node != nullptr)
        {
            st_TreeNode.push(node);
            result.push_back(node->val);
            node = node->right;
        }
        if (!st_TreeNode.empty())
        {
            node = st_TreeNode.top();
            st_TreeNode.pop();
            node = node->left;
        }
    }
    Reverse(result);
    return result;
}
中序遍历(只有方法二)
C++
vector<int> inorderTraversal(TreeNode* root)
{
    vector<int> result;
    if (root == nullptr)
        return result;
    stack<TreeNode*> st_TreeNode;
    TreeNode* node = root;
    while (node != nullptr || !st_TreeNode.empty())
    {
        while (node != nullptr)
        {
            st_TreeNode.push(node);
            node = node->left;
        }
        if (!st_TreeNode.empty())
        {
            node = st_TreeNode.top();
            st_TreeNode.pop();
            result.push_back(node->val);
            node = node->right;
        }
    }
    return result;
}
层序遍历(BFS)
C++
vector<vector<int>> levelOrder(TreeNode* root)
{
    vector<vector<int>> result;
    if (root == nullptr)
        return result;
    queue<TreeNode*> que_TreeNode;
    que_TreeNode.push(root);
    while (!que_TreeNode.empty())
    {
        int size = que_TreeNode.size();
        vector<int> temp;
        for (int i = 0; i != size; ++i)
        {
            TreeNode* node = que_TreeNode.front();
            que_TreeNode.pop();
            temp.push_back(node->val);
            if (node->left != nullptr)
                que_TreeNode.push(node->left);
            if (node->right != nullptr)
                que_TreeNode.push(node->right);
        }
        result.push_back(temp);
    }
    return result;
}

排序

交换swap

C++
void swap(int& a, int& b)
{
    int temp = a;
    a = b;
    b = temp;
}

void swap(int& a, int& b)
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

冒泡排序 O(n^2)

isSort:是否已经有序的标志

C++
void Bubble_Sort(vector<int>& nums)
{
    for (int end = nums.size() - 1; end > 0; end--)
    {
        bool isSort = true;
        for (int i = 0; i < end; i++)
        {
            if (nums[i] > nums[i + 1])
            {
                swap(nums[i], nums[i + 1]);
                isSort = false;
            }
        }
        if (isSort) break;
    }
}

border:记录上一次最后交换的位置,下一轮交换只需要进行到这个位置即可

C++
void Bubble_Sort(vector<int>& nums)
{
    for (int end = nums.size() - 1; end > 0; end--)
    {
        int border = 0;
        for (int i = 0; i < end; i++)
        {
            if (nums[i] > nums[i + 1])
            {
                swap(nums[i], nums[i + 1]);
                border = i + 1;
            }
        }
        end = border;
    }
}

鸡尾酒排序:定向冒泡排序,同时的冒泡两边

C++
void Bubble_Sort(vector<int>& nums)
{
    int left = 0, right = nums.size() - 1;
    while (left < right)
    {
        for (int i = left; i < right; i++)
        {
            if (nums[i] > nums[i + 1])
            {
                swap(nums[i], nums[i + 1]);
            }
        }
        right--;

        for (int i = right; i > left; i--)
        {
            if (nums[i] < nums[i - 1])
            {
                swap(nums[i - 1], nums[i]);
            }
        }
        left++;
    }
}

选择排序 O(n^2)

每次从待排序列中选出一个最小值,然后放在序列的起始位置,直到全部待排数据排完即可。

C++
void Select_Sort(vector<int>& nums)
{
    for (int i = 0; i < nums.size(); i++)
    {
        int start = i, min = i;
        while (start < nums.size())
        {
            if (nums[start] < nums[min]) min = start;
            start++;
        }
        if (min != i) swap(nums[i], nums[min]);
    }
}

插入排序 O(n^2)

在待排序的元素中,假设前n-1个元素已有序,现将第n个元素插入到前面已经排好的序列中,使得前n个元素有序。按照此法对所有元素进行插入,直到整个序列有序。

C++
void Insert_Sort(vector<int>& nums)
{
    for (int i = 1; i < nums.size(); i++)
    {
        int insert_value = nums[i];
        int insert_pos = i;
        while (insert_pos > 0 && nums[insert_pos - 1] > insert_value)
        {
            nums[insert_pos] = nums[insert_pos - 1];
            insert_pos--;
        }
        nums[insert_pos] = insert_value;
    }
}

归并排序 O(nlogn)

将已有序的子序合并,从而得到完全有序的序列,即先使每个子序有序,再使子序列段间有序。

C++
void merge(vector<int>& nums, int left, int mid, int right) // 左闭右闭
{
    vector<int> left_nums;
    vector<int> right_nums;

    for (int i = left; i <= right; i++)
    {
        if (i <= mid) left_nums.push_back(nums[i]);
        else right_nums.push_back(nums[i]);
    }

    int i = 0, j = 0, k = left;
    while (i < left_nums.size() && j < right_nums.size())
    {
        if (left_nums[i] < right_nums[j])
        {
            nums[k] = left_nums[i];
            k++;
            i++;
        }
        else
        {
            nums[k] = right_nums[j];
            k++;
            j++;
        }
    }
    while (i < left_nums.size())
    {
        nums[k] = left_nums[i];
        k++;
        i++;
    }
    while (j < right_nums.size())
    {
        nums[k] = right_nums[j];
        k++;
        j++;
    }
}

void Merge_Sort(vector<int>& nums, int left, int right) // 左闭右闭
{
    if (left == right) return;
    else
    {
        int mid = left + (right - left) / 2;
        Merge_Sort(nums, left, mid);
        Merge_Sort(nums, mid + 1, right);
        merge(nums, left, mid, right);
    }
}

堆排序 O(nlogn)

C++
void heapify(vector<int>& nums, int nums_size, int root)
{
    if (root >= nums_size) return;
    int left_child = root * 2 + 1;
    int right_child = root * 2 + 2;
    int max = root;
    if (left_child < nums_size && nums[left_child] > nums[max])
    {
        max = left_child;
    }
    if (right_child < nums_size && nums[right_child] > nums[max])
    {
        max = right_child;
    }
    if (max != root)
    {
        swap(nums[root], nums[max]);
        heapify(nums, nums_size, max);
    }
}

void Heap_Sort(vector<int>& nums)
{
    // build heap
    int last_node = nums.size() - 1;
    int parent = (last_node - 1) / 2;
    for (int i = parent; i >= 0; i--)
    {
        heapify(nums, nums.size(), i);
    }

    // sort
    for (int i = nums.size() - 1; i >= 0; i--)
    {
        swap(nums[i], nums[0]);
        heapify(nums, i, 0);
    }
}

希尔排序

先选定一个小于nums.size()的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作…

当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。

C++
void Shell_Sort(vector<int>& nums)
{
    // 初始增量gap=len/2,每一趟之后除以二
    for (int gap = nums.size() / 2; gap > 0; gap /= 2)
    {
        //插入排序
        for (int i = gap; i < nums.size(); i++)
        {
            int insert_value = nums[i];
            int insert_pos = i;
            while (insert_pos >= gap && nums[insert_pos - gap] > insert_value)
            {
                nums[insert_pos] = nums[insert_pos - gap];
                insert_pos -= gap;
            }
            nums[insert_pos] = insert_value;
        }
    }
}

快速排序 O(nlogn)

任取待排序元素序列中的某元素作为基准值,按照该基准值将待排序列分为两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右序列重复该过程,直到所有元素都排列在相应位置上为止。

C++
int partition(vector<int>& nums, int left, int right) // 左闭右闭
{
    int pivot = nums[right];
    int i = left;
    for (int j = left; j < right; j++)
    {
        if (nums[j] <= pivot)
        {
            swap(nums[i], nums[j]);
            i++;
        }
    }
    swap(nums[i], nums[right]);
    return i;
}

void Quick_Sort(vector<int>& nums, int left, int right) // 左闭右闭
{
    if (left < right)
    {
        int mid = partition(nums, left, right);
        Quick_Sort(nums, left, mid - 1);
        Quick_Sort(nums, mid + 1, right);
    }
}

二路快排

C++
void quicksort(vector<int>& nums, int left, int right) {
    if(left >= right) return;
    int pivot = nums[left];
    int i = left, j = right;
    while(i < j) {
        while(i < j && nums[j] > pivot) --j;
        while(i < j && nums[i] <= pivot) ++i;
        if(i < j) swap(nums[i], nums[j]);
    }
    swap(nums[left], nums[i]);
    quicksort(nums, left, i - 1);
    quicksort(nums, i + 1, right);
}

快速排序尽量使得分开的左右两区间大小一致,否则会退化为 n^2 的复杂度

  • 当数据已基本有序时,pivot默认选最左边或最右边时,相当于每次只排序一个元素,退化为选择排序,应随机选择一个元素作为 pivot。
C++
void quicksort(vector<int>& nums, int left, int right) {
    if(left >= right) return;
    srand(time(nullptr));
    int random = rand() % (right - left + 1) + left;
    swap(nums[left], nums[random]);
    int pivot = nums[left];
    int i = left, j = right;
    while(i < j) {
        while(i < j && nums[j] > pivot) --j;
        while(i < j && nums[i] <= pivot) ++i;
        if(i < j) swap(nums[i], nums[j]);
    }
    swap(nums[left], nums[i]);
    quicksort(nums, left, i - 1);
    quicksort(nums, i + 1, right);
}

三路快排

  • 当数据包含大量的重复元素,不论 pivot 如何选择,都会导致左右区间的大小严重失衡,应选择三路排序:分成三个区间,左(小于pivot) ,中(等于pivot),右( 大于pivot),这样左右区间的大小不会相差太大。
C++
void quicksort(vector<int>& nums, int left, int right) {
    if(left >= right) return;
    srand(time(nullptr));
    int random = rand() % (right - left + 1) + left;
    swap(nums[left], nums[random]);
    int pivot = nums[left];
    int i = left + 1, j = right; // 中区间 [i, j]
    for(int k = left + 1; k <= j;) {
        if(nums[k] < pivot) {
            swap(nums[k], nums[i]);
            ++i;
            ++k;
        } else if(nums[k] == pivot) {
            ++k;
        } else {
            swap(nums[k], nums[j]);
            --j;
        }
    }
    swap(nums[left], nums[i - 1]);
    quicksort(nums, left, i - 2);
    quicksort(nums, j + 1, right);
}