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 归并排序(个人觉得会考)
在这里插入代码片