高频重点算法题
- 前言
- **NC78** **反转链表**
- **NC76** **用两个栈实现队列**
- **NC68** **跳台阶**
- **NC4** **判断链表中是否有环**
- **NC66** **两个链表的第一个公共结点**
- **NC5** **二叉树根节点到叶子节点的所有路径和**
- NC86 **矩阵元素查找**
- **NC124** **字典树的实现**
前言
按照出现的频率进行练习,虽然很多题都是做过的,但是多做几遍之后,又发现了一些新的不错的思路。周末的小尾巴~。 题目来源于牛客网平台,一个不错的练习算法的平台。
如果觉得文章有帮助的,麻烦三连一下欧。
NC78 反转链表
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* newHead = nullptr;
while(pHead){
ListNode* nex = pHead->next;
pHead->next = newHead;
newHead = pHead;
pHead = nex;
}
return newHead;
}
};
NC76 用两个栈实现队列
压入的时候随便处理,弹出的时候借助辅助栈 导出来就可以了。
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
while(stack1.size() > 0){
stack2.push(stack1.top());
stack1.pop();
}
int val = stack2.top();
stack2.pop();
while(stack2.size() > 0){
stack1.push(stack2.top());
stack2.pop();
}
return val;
}
private:
stack<int> stack1;
stack<int> stack2;
};
扩展:
用两个队列实现栈:
弹出的时候不用管,压入的时候已经处理好了。
class MyStack{
public:
MyStack(){
while(!q1.empty()){
q1.pop();
}
while(!q2.empty()){
q2.pop();
}
}
void push(int x){
if(q1.size() >= 1){
//q1.push(x);
// 将q1的元素倒入到q2中
while(!q1.empty()){
int temp = q1.front();
q1.pop();
q2.push(temp);
}
q1.push(x);
// 将q2的元素 倒回q1
while(!q2.empty()){
int temp1 = q2.front();
q2.pop();
q1.push(temp1);
}
}else{
q1.push(x);
}
}
void pop(){
q1.pop();
}
int top(){
return q1.front();
}
private:
queue<int> q1;
queue<int> q2;// 临时存储
};
NC68 跳台阶
这道题其实就是一道斐波那契函数。。。
class Solution {
public:
int jumpFloor(int n) {
vector<int> dp(n+1,0);
dp[0] = 1;
for(int i = 1; i <=n; i++){
for(int j = 1; j <=2; j++){
// 判断条件不是必要的 在这道题里 只是为了模板化
if(i - j >=0 )dp[i] += dp[i - j];// 注意从1开始
}
}
return dp[n];
}
};
扩展版:
跳台阶升级版
class Solution {
public:
int jumpFloor(int n) {
vector<int> dp(n+1,0);
dp[0] = 1;
for(int i = 1; i <=n; i++){
for(int j = 1; j <=n; j++){
if(i - j >=0 )dp[i] += dp[i - j];
}
}
return dp[n];
}
};
NC4 判断链表中是否有环
快慢指针的思路
class Solution {
public:
bool hasCycle(ListNode *head) {
// 特殊情况 当链表长度为空为1无环
// 有环 必然会相遇
// 无环 则必然会遇到空节点
if(head == nullptr || head->next == nullptr){
return false;
}
ListNode* fast = head->next;
ListNode* slow = head;
while(fast->next!=nullptr && fast->next->next!=nullptr){
if(slow == fast){
return true;
}else{
fast = fast->next->next;
slow = slow->next;
}
}
return false;
}
NC66 两个链表的第一个公共结点
思路:
走你走过的路,总会相遇
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr || pHead2 == nullptr) return nullptr;
ListNode* A = pHead1;
ListNode* B = pHead2;
while(A!=B){
A = A != nullptr? A->next:pHead2;
B = B != nullptr? B->next:pHead1;
}
return A;
}
};
NC5 二叉树根节点到叶子节点的所有路径和
描述
给定一个二叉树的根节点root,该树的节点值都在数字\ 0-9 0−9 之间,每一条从根节点到叶子节点的路径都可以用一个数字表示。
1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点
2.叶子节点是指没有子节点的节点
3.路径只能从父节点到子节点,不能从子节点到父节点
4.总节点数目为n
例如根节点到叶子节点的一条路径是1\to 2\to 31→2→3,那么这条路径就用\ 123 123 来代替。
找出根节点到叶子节点的所有路径表示的数字之和
结题思路:
简单的前序遍历,就可以完成。
处理节点: sum = sum*10 + node->val;
class Solution {
public:
/**
*
* @param root TreeNode类
* @return int整型
*/
int sumNumbers(TreeNode* root) {
// write code here
if(root == nullptr) return 0;
int sum = 0;
return preOrder(root,sum);
}
int preOrder(TreeNode* node, int sum){
if(node == nullptr){
return 0;
}
sum = sum*10 + node->val;
if(node->left == nullptr && node->right == nullptr){
return sum;
}
return preOrder(node->left, sum) + preOrder(node->right, sum);
}
};
NC86 矩阵元素查找
方法一:暴力 不符合时间复杂度要求。
class Solution {
public:
vector<int> findElement(vector<vector<int> > mat, int n, int m, int x) {
// write code here
// 两层for循环就可以找到
vector<int> res;
for(int i = 0; i < n; ++i){
for(int j = 0; j < m; ++j){
if(mat[i][j] == x){
return vector<int>{i, j};
}
}
}
return res;
}
};
这道题和一道剑指Offer的题目很像。
贪心策略:
- 如果当前位置的数大于目标值: 那么根据该矩阵的特性, 其右边的数只可能更大, 我们往上走一步.
- 如果当前位置的数小于目标值: 那么上边的数只可能更小, 我们往右走.
class Solution {
public:
vector<int> findElement(vector<vector<int> > mat, int n, int m, int x) {
// write code here
// 两层for循环就可以找到
vector<int> res;
int i = m-1, j = 0;
while(i >=0 && j < n){
//cout<<mat[j][i]<<" ";
if(mat[j][i] < x) j++;
else if(mat[j][i] > x) i--;
else{
return vector<int>{j,i};
}
}
return res;
}
};
NC124 字典树的实现
这道题实现起来确实是比较难得,已经偏向设计题的要求了。
使用map存储所给的字符串中字母出现的个数
类似链表的结构
每个节点中存放:
- 是否为尾节点
- 出现数量
- 下一个节点的字符以及节点值
插入
删除
搜索
计算前缀
struct Node{
bool isEnd;
int countNum;
map<char, Node*> children;
Node():isEnd(false), countNum(0){};
};//
class Solution {
public:
Node* root = new Node();
void insertTrie(string word){
Node* node = root;
for(char c : word){
if(node->children.find(c) == node->children.end()){
// 如果找不到,则插入新的
node->children[c] = new Node();
}
// 这个字符串 的数量+1
node->children[c]->countNum += 1;
// 移动node位置
node = node->children[c];
}
node->isEnd = true;
}
void deleteTrie(string word){
Node* node = root;
// 删除主要有三种情况
// 一种是找不到的情况, 一种是刚好只出现一次 一种是出现次数大于1
for(char c : word){
if(node->children.find(c) == node->children.end())
return ;
else if(node->children[c]->countNum == 1){
node->children.erase(c);
return;
}
node->children[c]->countNum -=1;
node = node->children[c];
}
}
bool searchTrie(string word){
Node* node = root;
for(char c : word){
if(node->children.find(c) == node->children.end()) return false;
node = node->children[c];
}
return node->isEnd;
}
int prefixNumber(string pre){
Node* node = root;
for(char c : pre){
if(node->children.find(c) == node->children.end()) return 0;
node = node->children[c];
}
return node->countNum;
}
/**
*
* @param operators string字符串vector<vector<>> the ops
* @return string字符串vector
*/
vector<string> trieU(vector<vector<string> >& operators) {
// write code here
vector<string> result;
if(operators.size() == 0) return result;
for(int i = 0; i < operators.size(); i++){
if(operators[i][0] == "1") insertTrie(operators[i][1]);
else if(operators[i][0] == "2") deleteTrie(operators[i][1]);
else if(operators[i][0] == "3")
if(searchTrie(operators[i][1]))
result.push_back("YES");
else
result.push_back("NO");
else if(operators[i][0] == "4")
result.push_back(to_string(prefixNumber(operators[i][1])));
}
return result;
}
};