数据结构和算法
1、给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
给定整数数组 A 和 常数 m, 输出 A 中满足数列和为 m 的连续子序列个数 示例 输入: A = [1,2,3], m=3 输出: 2
思路:
1)累计求和,双层遍历求差是否为目标值,得到满足条件的个数
class Solution { public: int subarraySum(vector<int>& nums, int k) { int n=nums.size(); vector<int> sum(n+1,0); for(int i=1;i<=n;i++) sum[i]=sum[i-1]+nums[i-1]; int count=0; for(int i=0;i<n;i++){ for(int j=i;j<n;j++){ if(sum[j+1]-sum[i]==k) count+=1; } } return count; } };
2)哈希表思路,累计求和放在哈希表中,然后看求和的数和目标值的差是否在哈希表中存在,以及存在的个数,以此求和得到满足条件的个数,初始化hash[0]=1
class Solution { public: int subarraySum(vector<int>& nums, int k) { unordered_map<int, int> hash; int sum = 0, ans = 0; hash[0] = 1; for(int num : nums) { sum += num; ans += hash[sum - k]; hash[sum]++; } return ans; } };
2、反转链表
示例:
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
// 递归方式 class Solution { public: ListNode* reverseList(ListNode* head) { //结束条件 if(head == NULL || head->next == NULL) return head; //递归反转剩下的元素 ListNode* rev = reverseList(head->next); //头节点变为最后一个节点 head->next->next = head; //头节点指针设置为空 head->next = NULL; return rev; } };
2)非递归方式
class Solution { public: ListNode* reverseList(ListNode* head) { if(head==NULL || head->next == NULL){ return head; } //前面一个节点 ListNode* pre = nullptr; //当前节点 ListNode* cur = head; while(cur != nullptr) { //指针先向后遍历 ListNode* tmp = cur->next; //改变指针指向 cur->next = pre; //前面一个节点变为当前节点、当前节点变为后面的节点 pre = cur; cur = tmp; } return pre; } };
3、给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* merge(ListNode* l1, ListNode* l2){ //合并L1,L2两个有序链表 if(!l1) return l2; if(!l2) return l1; if(l1->val < l2->val){ l1->next = merge(l1->next, l2); return l1; }else{ l2->next = merge(l1, l2->next); return l2; } } ListNode* mergeKLists(vector<ListNode*>& lists) { int interval = 1; int len = lists.size(); if(len==1){ return lists[0]; } if(len==0){ return nullptr; } while(interval<len){ for(int i=0;i<len-interval;){ lists[i]=merge(lists[i],lists[i+interval]); i=i+2*interval; } interval = interval*2; } return lists[0]; } };
4、给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:
输入:s = ""
输出:0
两种思路:
1)用栈实现
class Solution { public: int longestValidParentheses(string s) { int maxLen = 0; stack<int> st; st.push(-1); for (int i = 0; i < s.size(); i++) { char c = s[i]; if (c == '(') { // 左括号的索引,入栈 st.push(i); } else { // 遍历到右括号, 栈顶的左括号被匹配,出栈 st.pop(); if (st.size()!=0) { // 栈未空,计算有效连续长度 int curMaxLen = i - st.top(); // 挑战最大值 maxLen = max(maxLen, curMaxLen); } else { // 栈空了,入栈充当参照 st.push(i); } } } return maxLen; } };
2)动态规划思路:求最值问题
5、我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”:
B.length >= 3
存在 0 < i < B.length - 1 使得 B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
(注意:B 可以是 A 的任意子数组,包括整个数组 A。)
给出一个整数数组 A,返回最长 “山脉” 的长度。
如果不含有 “山脉” 则返回 0。
示例 1:
输入:[2,1,4,7,3,2,5]
输出:5
解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
示例 2:
输入:[2,2,2]
输出:0
解释:不含 “山脉”。
class Solution { public: int longestMountain(vector<int>& arr) { int len = arr.size(); vector<int> dp(len,0); for(int i=1;i<len;i++){ if(arr[i]>arr[i-1]&&dp[i-1]==0){ dp[i]=max(dp[i-1]+2,0); }else if(arr[i]>arr[i-1]){ dp[i]=max(dp[i-1]+1,0); } } vector<int> dp2(len,0); for(int i=len-1;i>0;i--){ if(arr[i]<arr[i-1]&&dp2[i]==0){ dp2[i-1]=max(dp2[i]+2,dp2[i]); }else if(arr[i]<arr[i-1]){ dp2[i-1]=max(dp2[i]+1,dp2[i]); } } int res = 0; if(dp[len-1]==len||dp2[0]==len){ return 0; } for(int i=0;i<len;i++){ if(dp[i]>=2&&dp2[i]>=2 && (dp[i]+dp2[i]>res)){ res = dp[i]+dp2[i]-1; } } return res; } };
6、单词搜索(leetcode 212,79)
给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
思路:回溯算法
class Solution { public: int h; int w; vector<string> findWords(vector<vector<char>>& board, vector<string>& words) { vector<string> res; h = board.size(); w = board[0].size(); for(int k=0;k<words.size();k++){ for(int i = 0; i < h; i++){ for(int j = 0; j < w; ++j){ //尝试从每一个字母开始回溯 if(searchexist(board, words[k], 0, i, j)){ res.push_back(words[k]); i = h; j = w; } } } } return res; } int searchexist(vector<vector<char>>& board, string &word, int n, int x, int y){ if(x < 0 || x > h-1 || y < 0 || y > w-1 || word[n] != board[x][y]) return 0; //达成要求返回1 if(n == word.length()-1) return 1; //将用过的保存起来 char temp = board[x][y]; board[x][y] = 0; //四个方回溯 int flag = searchexist(board, word, n+1, x+1, y) ||searchexist(board, word, n+1, x-1, y) ||searchexist(board, word, n+1, x, y+1) ||searchexist(board, word, n+1, x, y-1); //回溯失败,将其重置 board[x][y] = temp; return flag; } };