【408数据结构】备考常见必会算法图鉴

408算法题

提示:代码C++,部分引用自leetcode



提示:以下是本篇文章正文内容,下面案例可供参考

一、链表

1.1链表逆置

方法1.迭代

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* curr = head;
        while (curr) {
            ListNode* next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

方法2.递归

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) {
            return head;
        }
        ListNode* newHead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newHead;
    }
};


1.2删除链表倒数第n个结点

方法1:双指针,使用快慢指针

//哑结点其实就是放在第一个存放数据结点之前、头结点之后的结点。加入哑结点之后就可以使所有数据结点都有前驱结点,这样就会方便执行链表的一些操作(比如删除)

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
    	//在头结点之前设置哑结点
        ListNode* dummy = new ListNode(0, head);
        ListNode* first = head;
        //慢的用哑结点赋值,与快的同步往后会相差一位 便于删除
        ListNode* second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first->next;
        }
        while (first) {
            first = first->next;
            second = second->next;
        }
        second->next = second->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};


1.3 给定两个增序的链表,试将其合并成一个增序的链表。

样例
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4

方法1:递归

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) {
            return l2;
        } else if (l2 == nullptr) {
            return l1;
        } else if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

方法2:迭代

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* preHead = new ListNode(-1);

        ListNode* prev = preHead;
        while (l1 != nullptr && l2 != nullptr) {
            if (l1->val < l2->val) {
                prev->next = l1;
                l1 = l1->next;
            } else {
                prev->next = l2;
                l2 = l2->next;
            }
            prev = prev->next;
        }

        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev->next = l1 == nullptr ? l2 : l1;

        return preHead->next;
    }
};

1.4 删除有序(递增)链表中的重复元素。

leetcode:83
输入:head = [1,1,2]
输出:[1,2]

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (!head) {
            return head;
        }

        ListNode* cur = head;
        while (cur->next) {
            if (cur->val == cur->next->val) {
                cur->next = cur->next->next;
            }
            else {
                cur = cur->next;
            }
        }

        return head;
    }
};


1.5 以 O(1) 的空间复杂度,判断链表是否回文。

leetcode:234
输入:head = [1,2,2,1]
输出:true

方法1:非O(1)
借助辅助栈,将链表值放入栈中,遍历看两个值是否相等

//借用辅助栈
class Solution {
public:
    bool isPalindrome(ListNode* head) 
    {
        ListNode* cur = head;
        stack<int> s;
        while (cur)
        {
            s.push(cur->val);
            cur = cur->next;
        }

        while (!s.empty())
        {
            if (s.top() != head->val)
                return false;
            s.pop();
            head = head->next;
        }

        return true;
    }
};

方法2:递归 递归也不满足O(1)

方法3:双指针
使用快慢指针找到中点,后半段翻转,看是否相同

class Solution {
public:
// 主函数
    bool isPalindrome(ListNode* head) 
    {
        if (!head || !head->next) 
        {
            return true; 
        }
        ListNode *slow = head, *fast = head;
        while (fast->next && fast->next->next) 
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        //由于链表的长度有奇数和偶数之分
        //在奇数时slow在中间
        //偶数时slow在中间两个数的第一个
        //所以他们需要翻转后续的结点
        slow = reverseList(slow->next);
        while (slow) 
        {
            if (head->val != slow->val)
            {
                return false; 
            }  
            head = head->next;
            slow = slow->next;
        }
        return true; 
    }       
     //逆置
    ListNode* reverseList(ListNode* head) {
        ListNode *prev = nullptr, *next;
        while (head) {
            next = head->next;
            head->next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }
};                          

1.6 已排序链表,删除所有重复元素(保留一个)

Leeetcode 83

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (!head) {
            return head;
        }

        ListNode* cur = head;
        while (cur->next) {
            if (cur->val == cur->next->val) {
                cur->next = cur->next->next;
            }
            else {
                cur = cur->next;
            }
        }

        return head;
    }
};

1.7 给定两个链表,判断它们是否相交于一点,并求这个相交节点。

a+b+c = a+c+b

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) {
            return nullptr;
        }
        ListNode *pA = headA, *pB = headB;
        while (pA != pB) {
            pA = pA == nullptr ? headB : pA->next;
            pB = pB == nullptr ? headA : pB->next;
        }
        return pA;
    }
};

二、栈与队列

栈与队列一般不作为单独算法考点,只会以辅助栈和辅助队列的形式出现,算法不予以统计


三、树与二叉树

3.1 [递归]二叉树的深度

class Solution{
public:
	int maxDepth(TreeNode* root)
	{
		return 1+man(maxDepth(root->left),maxDepth(root->right)):0;
	}
};

3.2 [递归][树的判定]翻转平衡二叉树

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) {
            return nullptr;
        }
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);
        root->left = right;
        root->right = left;
        return root;
    }
};


3.3 [递归][树的判定]平衡二叉树的判定

Leetcode
概念:左右子树高度差小于等于1
思路:类似于求树的深度,但有两个不同的地方:一是我们需要先处理子树的深度再进行
比较,二是如果我们在处理子树时发现其已经不平衡了,则可以返回一个-1。

class Solution {
public:
    int height(TreeNode* root) {
        if (root == NULL) {
            return 0;
        } else {
            return max(height(root->left), height(root->right)) + 1;
        }
    }

    bool isBalanced(TreeNode* root) {
        if (root == NULL) {
            return true;
        } else {
        	//当前结点的左右平衡因子小于等于1,递归判断其左右子树是否满足平衡二叉树
            return abs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
        }
    }
};


3.4 [递归]二叉树的直径

Leetcode
注意 int&的用法

class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
        int diameter = 0;
        helper(root, diameter);
        return diameter;
    }
    //辅助函数  注意int& 转为引用类型,使得实参双向传递
    int helper(TreeNode* node,int& diameter)
    {
        //终止条件
        if(node == nullptr)return 0;
        //返回左右子树的高度
        int l = helper(node->left, diameter);
        int r = helper(node->right, diameter);
        //比较此时左右子树的和与最大值的大小
        diameter = max(l + r, diameter);
        return max(l, r) + 1;     
    }
};

3.5 [双递归]二叉树的路径总和

Leetcode
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
辅助函数:计算连续加入节点的路径。

class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        return root==nullptr?0:dfs(root,targetSum)+dfs(root->left,targetSum)+dfs(root->right,targetSum);
    }
    int dfs(TreeNode* root, int targetSum){
        if(!root)return 0;
        //注意此时相等不能返回,
        int count = (root->val == targetSum)? 1: 0;
        count+=dfs(root->left,targetSum-root->val);
        count+=dfs(root->right,targetSum-root->val);
        return count;
    }

};

3.6 [递归]判断一个二叉树是否对称

辅助函数:判断两棵子树是否相等
“四步法”:
(1)如果两个子树都为空指针,则它们相等或对称
(2)如果两个子树只有一个为空指针,则它们不相等或不对称
(3)如果两个子树根节点的值不相等,则它们不相等或不对称
(4)根据相等或对称要求,进行递归处理。

class Solution {
public:
    bool check(TreeNode *p, TreeNode *q) {
        if (!p && !q) return true;
        if (!p || !q) return false;
        return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
    }

    bool isSymmetric(TreeNode* root) {
        return check(root, root);
    }
};


3.7 [遍历][递归]二叉树的前中后序遍历

class Solution {
public:
	//前序
    void preorder(TreeNode *root, vector<int> &res) {
        if (root == nullptr) {
            return;
        }
        res.push_back(root->val);
        preorder(root->left, res);
        preorder(root->right, res);
    }
	//中序
    void inorder(TreeNode* root, vector<int>& res) {
        if (!root) {
            return;
        }
        inorder(root->left, res);
        res.push_back(root->val);
        inorder(root->right, res);
    }
    //后序
    void aftorder(TreeNode *root, vector<int> &res) {
        if (root == nullptr) {
            return;
        }
        aftorder(root->left, res);
        aftorder(root->right, res);
        res.push_back(root->val);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        inorder(root, res);
        return res;
    }
};

3.8 [遍历][迭代]二叉树的前序遍历

借助辅助栈

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }

        stack<TreeNode*> stk;
        TreeNode* node = root;
        while (!stk.empty() || node != nullptr) {
            while (node != nullptr) {
                res.emplace_back(node->val);
                stk.emplace(node);
                node = node->left;
            }
            node = stk.top();
            stk.pop();
            node = node->right;
        }
        return res;
    }
};

3.9 [遍历]二叉树的层序遍历

借助辅助队列 BFS

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector <vector <int>> ret;
        if (!root) {
            return ret;
        }

        queue <TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            int currentLevelSize = q.size();
            ret.push_back(vector <int> ());
            for (int i = 1; i <= currentLevelSize; ++i) {
                auto node = q.front(); q.pop();
                ret.back().push_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
        }
        
        return ret;
    }
};


四、图(考的较少)


五、排序

5.1 [插入类]直接插入排序

void InsertSort(int arr[], int n){
    int i, j, temp;
    //遍历数组
    for (i = 1; i < n; i++){
        //当前元素的前驱大于当前元素
        if (arr[i - 1] > arr[i]) {
            //保存arr[i]
            temp = arr[i];
            //将前面有序序列中的所有大于arr[i]的值全部后移一位
            for (j = i - 1; j >= 0 && arr[j] > temp; j--){
                arr[j + 1] = arr[j];
            }
            //将原arr[i]赋值给arr[j + 1]
            arr[j + 1] = temp;
        }//if
    }//for
}

5.2 [插入类]希尔排序

void ShellSort(int arr[], int n) {
    int d;
    //初始化d为n/2,每一轮d为上一轮的1/2
    for (d = n / 2; d >= 1; d /= 2) {
        //从每个个分组的第二个元素开始,遍历剩余元素,进行直接插入排序
        for (int i = 1 + d; i < n; i++) {
            //直接插入排序(组间元素对比)
            if (arr[i] < arr[i - d]) {
                arr[0] = arr[i];
                int j;
                for (j = i - d; j > 0 && arr[j] > arr[0]; j -= d) {
                    arr[j + d] = arr[j];
                }
                arr[j + d] = arr[0];
            }
        }
    }
}

5.3 [交换类]冒泡排序

//交换
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}
//冒泡排序
void BubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        //标记此轮循环中是否进行了交换
        bool flag = false;
        //每次从数组中最后一个元素向前遍历,直到第i个元素
        for (int j = n - 1; j >= i; j--) {
            //将相邻两个逆序元素交换为正序,并更改flag
            if (arr[j - 1] > arr[j]) {
                swap(arr[j - 1], arr[j]);
                flag = true;
            }
        }//for
        //此次循环元素都是正序,则结束函数
        if (!flag) return;
    }//for
}

5.3 [交换类]快排

分治法

void QuickSort(vector<int>& v, int low, int high)
{
    if (low >= high)        // 结束标志
        return;
    int first = low;        // 低位下标
    int last = high;        // 高位下标
    int key = v[first];     // 设第一个为基准
    while (first < last)
    {
        // 将比第一个小的移到前面
        while (first < last && v[last] >= key)
            last--;
        if (first < last)
            v[first++] = v[last];
        // 将比第一个大的移到后面
        while (first < last && v[first] <= key)
            first++;
        if (first < last)
            v[last--] = v[first];
    }
        // 基准置位
    v[first] = key;
    // 前半递归
    QuickSort(v, low, first - 1);
    // 后半递归
    QuickSort(v, first + 1, high);
}


5.4 [选择类]简单选择

//交换元素
void swap(int &a, int &b){
    int temp = a;
    a = b;
    b = temp;
}
 
//简单选择排序
void SelectSort(int arr[], int n){
    //遍历数组
    for (int i = 0; i < n; i++){
        //标记当前数组中最小值元素的下标,初始为第 i 个元素
        int min = i;
        //从i + 1开始遍历数组
        for (int j = i + 1; i < n; j++){
            if (arr[j] < arr[min]) min = j;
        }
        //交换第 i 个元素和最小值元素
        swap(arr[i] , arr[j]);
    }
}

5.5 归并排序(个人觉得会考)

leetcode

在这里插入代码片

总结

  • 4
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值