算法编程题
string 相加 & 链表相加
// 计算 "19" + "2" 得到 "21"
string addStrings(string num1, string num2) {
int n1 = num1.size()-1, n2 = num2.size()-1;
string ans;
int carry = 0; //进位
while(n1 >= 0 || n2 >= 0 || carry > 0) {
int v1 = 0, v2 = 0;
if (n1 >= 0) v1 = num1[n1--] - '0';
if (n2 >= 0) v2 = num2[n2--] - '0';
int sum = v1 + v2 + carry;
char tmp = sum%10 + '0';
ans = tmp + ans; // 注意!!! 不是ans + tmp 而是 tmp + ans
carry = sum/10;
}
return ans;
}
Leetcode链表相加
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *p1 = l1, *p2 = l2;
ListNode *phead = new ListNode(-1);
ListNode *ans = phead;
int carry = 0;
while(p1 || p2 || carry > 0) {
int v1 = 0, v2 = 0;
if (p1) {
v1 = p1->val;
p1 = p1->next;
}
if (p2) {
v2 = p2->val;
p2 = p2->next;
}
int sum = v1 + v2 + carry;
ListNode *tmp = new ListNode(sum%10);
ans->next = tmp;
ans = ans->next;
carry = sum/10;
}
return phead->next;
}
两数之和,三数之和… N个数之和
- Leetcode题目 : 不使用运算符 + 和 - ,计算两整数 a 、b 之和。
int getSum(int a, int b) {
// a^b 异或,可以实现相加但不进位
// (a&b)<<1可以实现进位但不想加,
// 以上两者相加即可
return b==0? a : getSum(a^b, ((unsigned int)(a&b))<<1);
}
- Leetcode 返回两数的下标
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
int n = nums.size();
if (n < 2) return ret;
std::unordered_map<int,int> hashm;
for (int i = 0; i < n; ++i) {
// hashm[target - nums[i]]不等于0说明 target-nums[i]这个值 在之前的遍历已经出现
// hashm[target - nums[i]]不等于 i+1 说明 不是当前正在遍历的数字,这是为了避免重复
if (hashm[target - nums[i]] // 如果找不到就默认是0,(初始化0)
&& hashm[target - nums[i]] != i+1) {
ret.push_back(i);
ret.push_back(hashm[target - nums[i]]-1);
return ret;
} else {
hashm[nums[i]] = i+1; // 注意
}
}
return ret;
}
- Leetcode 四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
以下解法适用求 任意个数之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end()); // 先排成升序数组
return nSumTarget(nums, 4, 0, target);
}
// 以下是计算 n个数之和的函数
// nSumTarget函数,计算数组nums 中是否有 n 个数的和为target, 把所有满足条件的n个数返回
//nums 目标数组,必须是升序数组
// start:从数组下标为start处开始只考虑后面的元素;
// target:n个数的目标和sum
vector<vector<int>> nSumTarget(vector<int>& nums, int n, int start, int target) {
int sz = nums.size();
vector<vector<int>> res;
if(n<2 || n>sz) return res;
if(n==2) {
res = twoSum(nums, start, target);
} else {
for(int i = start; i < sz; ++i){
int firstnum = nums[i];
vector<vector<int>> sub = nSumTarget(nums, n-1, i+1, target-firstnum);
for(auto &arr : sub) {
arr.push_back(firstnum);
res.push_back(arr);
}
while(i < sz-1 && nums[i+1] == nums[i]) i++; // 避免重复
}
}
return res;
}
// 以下是计算两数之和的函数, 注意数组nums 必须是已经排好序的升序数组,否则出错
// nums 目标数组; start:从数组下标为start处开始只考虑后面的元素; target:两数的目标和sum
vector<vector<int>> twoSum(vector<int>& nums, int start, int target) {
int l = start, r = nums.size()-1; // 只考虑 start 到 nums.size()-1这一段
vector<vector<int>> ans;
while(l<r){ // 双指针经典循环,左指针l从左往右移动,右指针r从右往左移动
int left = nums[l];
int right = nums[r];
int sum = left + right; // 当前 的 sum 和
if(sum < target) { //如果和比目标值小,那么 l 右移
while(l<r && nums[l] == left) l++; // 避免重复
} else if (sum > target) { //如果和比目标值小,那么 r 右移
while(l<r && nums[r] == right) r--; // 避免重复
} else { // 如果 和 等于目标值
ans.push_back({left, right}); // 那么 先计入结果数组中,
while(l<r && nums[l]==left) l++; // 然后让左指针l右移,右指针r左移,
while(l<r && nums[r]==right) r--; // 并且一定要注意用while 来避免重复
}
}
return ans;
}
};
LRU缓存机制
- 以下是使用哈希表和 STL 里的 list 链表容器实现
class Solution {
public:
/**
* lru design
* @param operators int整型vector<vector<>> the ops
* @param k int整型 the k
* @return int整型vector
*/
vector<int> LRU(vector<vector<int> >& operators, int k) {
// write code here
cap = k;
vector<int> ans;
for (auto &opt : operators) {
if (opt[0] == 1) put(opt.at(1), opt.at(2));
else if (opt[0] == 2) ans.push_back(get(opt.at(1)));
}
return ans;
}
private:
int cap;
list<pair<int, int>> kv; // 记录数据的链表
// 用哈希表记录 key值和对应的链表迭代器
unordered_map<int, list<pair<int, int>>::iterator> ump;
// put新数据进去,如果key已经存在,则更新,并将最新的放在链表头部
void put(int key, int value) {
auto itr = ump.find(key);
//如果已经存在,就先删除他,这样就可以和不存在做相同的处理了
if( itr != ump.end()) {
kv.erase(itr->second);
ump.erase(key);
}
pair<int, int> p{key, value};
// 新数据放在链表头部
kv.push_front(p);
// 数据要记录进哈希表
ump.emplace(key, kv.begin());
// 如果 size 大于规定的cap,就删除链表尾部元素
while (kv.size()>cap) {
ump.erase(kv.back().first);//注意 map删除元素方法,erase(key)
kv.pop_back();
}
}
// 获取已经存入的数据,没有就返回-1,
// 如果有就返回value,并且把对应的数据放到链表头部
int get(int key) {
if(ump.count(key) == 0) {
return -1; //如果没有就返回-1
}
auto iter = ump[key];
//将kv(可以是别的list)的iter迭代器指定的元素移到kv.begin()前面
kv.splice(kv.begin(), kv, iter);
return kv.begin()->second;
}
};
- 以下是使用哈希表和 自己实现的 双向 链表实现
struct dListNode {
int key, value;
dListNode *prev;
dListNode *next;
dListNode(): key(0), value(0), prev(NULL), next(NULL) {}
dListNode(int k, int v) : key(k), value(v), prev(NULL), next(NULL) {}
};
class LRUCache {
// 数据结构要求满足以下条件:
// get 要求要快速访问,unordered_map 哈希表 可以满足这个条件
// 当容量达到上限时,最久未使用(使用包括get,put增加新值或者修改旧值)的数据要删去,
// 这要求数据结构要满足快速删除,快速插入(快速转移位置),这就只能用 双向链表了。
// 双向链表越靠近tail,就表示越久没有使用,刚刚使用的一律remove然后add到head后面。
public:
unordered_map<int, dListNode *> key_node;
dListNode *head;
dListNode *tail;
int size;
int capacity;
LRUCache(int capacity) : size(0), capacity(capacity) {
head = new dListNode();
tail = new dListNode();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if(key_node.count(key)==0) return -1;
removeNode(key_node[key]);
addNodeToHead(key_node[key]);
return key_node[key]->value;
}
void put(int key, int value) {
if(key_node.count(key)!=0) {
removeNode(key_node[key]);
key_node[key]->value = value;
addNodeToHead(key_node[key]);
} else {
while (size >= capacity) {
key_node.erase(tail->prev->key);
removeNode(tail->prev);
size--;
}
dListNode *node = new dListNode(key, value);
addNodeToHead(node);
key_node[key] = node;
size++;
}
}
void removeNode(dListNode *node) {
node->next->prev = node->prev;
node->prev->next = node->next;
}
void addNodeToHead(dListNode *node) {
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
void addNodeToTail(dListNode *node) {
node->prev = tail->prev;
node->next = tail;
tail->prev->next = node;
tail->prev = node;
}
};
寻找两个正序数组的中位数
Leetcode题: 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int m = nums1.size();
int n = nums2.size();
//中位数 = (left + right)/2
int left = (m + n + 1) / 2;
int right = (m + n + 2) / 2;
return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
}
//在两个有序数组中找到第k个元素(例如找第一个元素,k=1,即nums[0])
//i: nums1的起始位置 j: nums2的起始位置(i,j都是从0开始)
int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k)
{
//若nums1为空(或是说其中数字全被淘汰了)
//在nums2中找第k个元素,此时nums2起始位置是j,所以是j+k-1
if(i >= nums1.size()) return nums2[j + k - 1];
//nums2同理
if(j >= nums2.size()) return nums1[i + k - 1];
//递归出口
if(k == 1) return std::min(nums1[i], nums2[j]);
//这两个数组的第K/2小的数字,若不足k/2个数字则赋值整型最大值,以便淘汰另一数组的前k/2个数字
int midVal1 = (i + k/2 - 1 < nums1.size()) ? nums1[i + k/2 - 1] : INT_MAX;
int midVal2 = (j + k/2 - 1 < nums2.size()) ? nums2[j + k/2 - 1] : INT_MAX;
//二分法核心部分
if(midVal1 < midVal2)
return findKth(nums1, i + k/2, nums2, j, k - k/2);
else
return findKth(nums1, i, nums2, j + k/2, k - k/2);
}
};
最长回文子串
Leetcode 给定一个字符串 s,找到 s 中最长的回文子串
string longestPalindrome(string s) {
int n = s.size();
if(n<2) return s;
vector<vector<bool>> dp(n, vector<bool>(n));
for(int i = 0; i < n; ++i) dp[i][i] = true;
//状态转移方程,dp[i][j]表示以i开始,以j结尾的子字符串是否为回文子串
// dp[i][j]为true的条件有(1)s[i]==s[j];(2) dp[i+1][j-1]为true;
// 临界条件,dp[i][i]为true, 且 j-i<3 时,不需要考虑以上的条件(2)
int start = 0, len = 1;
// 因为 dp[i][j]依赖于 dp[i+1][j-1],即依赖于其左下角的值,则循环方向如下。
// 从第0列往最后一列遍历,每列从对角线位置处往上遍历
for(int j = 0; j < n; ++j) {
for(int i = j+1; i >= 0; --i) {
if (j-i<3) {
if (s[i]==s[j] ) {
dp[i][j] = true;
if(j-i+1 > len) {
start = i;
len = j-i+1;
}
}
} else {
if(s[i]==s[j] && dp[i+1][j-1]) {
dp[i][j] = true;
if(j-i+1 > len) {
start = i;
len = j-i+1;
}
}
}
}
}
return s.substr(start, len);
}
滑动窗口:最小覆盖子串,无重复字符的最长子串
- Leetcode最小覆盖子串 : 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
string minWindow(string s, string t) {
unordered_map<char, int> window, need;
for(char &c : t) need[c]++; // 把目标字符串放到哈希表
int l = 0, r = 0; // 滑动窗口的左边界和右边界
int valid = 0; // 满足要求的字符
int start = 0, len = INT_MAX; // 符合要求的字符串的起点和长度
// 窗口滑动
// 窗口扩大循环,右扩
while(r < s.size()) {
char c = s[r]; // 即将处理的字符
r++; // 窗口右边界右移
if (need.count(c)) { // 如果 c 字符在目标字符串t 中出现
window[c]++; // window 记录窗口中目标字符串含有的字符,计数
if(window[c] == need[c]) // 如果对于字符c,窗口中含有的数目与 t中含有的数目相同,表示 满足数目要求的字符 增加了一个。
valid ++;
}
// 窗口先向右扩大,直到满足目标要求,然后窗口左边界右移以缩小窗口。
// 窗口缩小循环
while (valid == need.size()) {
if(r-l < len) { // 前面r++了,所以这里计算长度不需要加1了。
start = l; // 更新符合要求的字符串的起点和长度
len = r - l;
}
char d = s[l]; //即将移出窗口的字符
l++; //窗口左边界右移
if(need.count(d)) { // 如果即将移出窗口的字符d 含于t 中
if(window[d]==need[d])
//如果字符d,移出前,窗口的含有数目与t中数目相同,则移出后,符合数目要求的字符就要减 1 。窗口缩小循环就将会跳出,窗口右边界再开始右移。
valid--;
window[d]--;
}
}
}
return len == INT_MAX ? "" : s.substr(start, len);
}
- Leetcode 无重复字符的最长子串 : 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> window;
int l = 0, r = 0;
int len = 0;
while(r<s.size()) {
char c = s[r];
r++;
window[c]++;
while(window[c]>1) { // 当不符合要求时开始让窗口左边界右移
window[s[l]]--;
l++;
}
len = max(len, r-l);
}
return len;
}
正则表达式
bool isMatch(string s, string p) {
if(p.empty()) return s.empty(); // 如果p为空, 那么 s 如果不空则不匹配
int ns = s.size(), np = p.size();
bool first_match = !s.empty() && (s[0]==p[0] || p[0]=='.'); // 第一个字符是否相同
if (p.length()>=2 && p[1]=='*')
// // 如果 p有第2个字符且为 *
// (a) * 匹配了 0 个p[0]
// (b) * 匹配了1个p[0],如果s="abcd",p="a*" ,而 * 不是匹配0 个 p[0]那这个条件肯定是错的
// 如果 p=".*","*"可以匹配多个任意字符".",这里消耗一个
return isMatch(s, p.substr(2, np-2)) || // 表示 * 匹配了 0 个p[0]
(first_match && isMatch(s.substr(1,ns-1), p)); // 表示 * 匹配了 1个 p[0]。
else
return first_match && isMatch(s.substr(1,ns-1), p.substr(1, np-1)); // 没有*
}
螺旋矩阵
Leetcode题目: 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(!matrix.size()) return vector<int>();
vector<int> ans;
int up = 0;
int down = matrix.size() - 1;
int left = 0;
int right = matrix.at(0).size() - 1;
// 遍历一行删去一行,遍历一列就删去一列,通过控制以上 up, down, left, right 的值来操作删去行列。如下,简单,但容易出错。
while(true) {
for(int i = left; i <= right; ++i) ans.push_back(matrix[up][i]);
if(++up > down) break; // 易错,要检查
for(int i = up; i <= down; ++i) ans.push_back(matrix[i][right]);
if(--right < left) break; // 易错,要检查
for(int i = right; i >= left; --i) ans.push_back(matrix[down][i]);
if(--down < up) break; // 易错,要检查
for(int i = down; i >= up; --i) ans.push_back(matrix[i][left]);
if(++left > right) break; // 易错,要检查
}
return ans;
}
分数转小数
Leetcode题目:给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。如果小数部分为循环小数,则将循环的部分括在括号内。如果存在多个答案,只需返回 任意一个 。对于所有给定的输入,保证 答案字符串的长度小于 10^4 。
输入:numerator = 4, denominator = 333
输出:“0.(012)”
//小数部分如果余数出现两次就表示该小数是循环小数了
string fractionToDecimal(int numerator, int denominator) {
if(denominator==0) return "";//边界条件,分母为0
if(numerator==0) return "0";//边界条件,分子为0
string result;
//转换为longlong防止溢出
long long num = static_cast<long long>(numerator);
long long denom = static_cast<long long>(denominator);
//处理正负号,一正一负取负号
if((num>0)^(denom>0))result.push_back('-');
//分子分母全部转换为正数
num=llabs(num);denom=llabs(denom);
//处理整数部分
result.append(to_string(num/denom));
//处理小数部分
num%=denom; //获得余数
if(num==0)return result; //余数为0,表示整除了,直接返回结果
result.push_back('.'); //余数不为0,添加小数点
int index=result.size()-1; //获得小数点的下标
unordered_map<int,int> record; //map用来记录出现重复数的下标,然后将'('插入到重复数前面就好了
while(num&&record.count(num)==0){ //小数部分:余数不为0且余数还没有出现重复数字
record[num]=++index;
num*=10; //余数扩大10倍,然后求商,和草稿本上运算方法是一样的
result+=to_string(num/denom);
num%=denom;
}
if(record.count(num)==1){ //出现循环余数,我们直接在重复数字前面添加'(',字符串末尾添加')'
result.insert(record[num],"(");
result.push_back(')');
}
return result;
}
会议室2 && 合并区间
Leetcode 会议室2: 给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],…] (si < ei),为避免会议冲突,同时要考虑充分利用会议室资源,请你计算至少需要多少间会议室,才能满足这些会议安排
int minMeetingRooms(vector<vector<int>>& intervals) {
if(intervals.empty()) return 0;
// 先将数组排序,先比较 第一个元素,再比较第二个
sort(intervals.begin(), intervals.end());
// 建立优先队列 小顶堆,比较当前遍历时间段的左边界与之前时间段的右边界的最小值,判断是否需要开启新的房间。
// 注意当遇到需要从一堆值里面挑出最小或最大值,或者最小或者最大的 k 个值,就可以考虑 优先队列。
priority_queue<int, vector<int>, greater<int>> pq;
pq.push(intervals[0][1]);
for(int i = 1; i < intervals.size(); ++i) {
int lessright = pq.top();
if(intervals[i][0] >= lessright) {
pq.pop();
}
pq.push(intervals[i][1]);
}
return pq.size();
}
Leetcode合并区间: 给出一个区间的集合,请合并所有重叠的区间。
vector<vector<int>> merge(vector<vector<int>>& intervals) {
int n = intervals.size();
vector<vector<int>> ans;
//注意vector是可以直接比较大小的,以第0个元素为准比较,如果相同,就以后一个元素为准
sort(intervals.begin(), intervals.end());
//先将各个区间以左边界 升序排列,左边界相同就以右边界升序排,那么可能融合的区间凑到一起去了。
for(int i = 0; i < n; ++i) {
int l = intervals[i][0], r = intervals[i][1];
if(!ans.size() || l > ans.back()[1]) { // 注意 ans.back() 是ans最后一个元素的引用
ans.push_back(intervals[i]);
} else {
ans.back()[1] = max(ans.back()[1], r);
}
}
return ans;
}
最大数
Leetcode题目:给定一组非负整数 nums,重新排列它们每个数字的顺序(每个数字不可拆分)使之组成一个最大的整数。
//注意灵活使用字符串按 字典序 比较大小!
struct {
bool operator() (const string &a, const string &b) {
return a + b > b + a;
}
} comp; // 仿函数
string largestNumber(vector<int>& nums) {
if(std::all_of(nums.begin(), nums.end(), [](int x){return x==0;})) return "0";
vector<string> ans(nums.size()); // 注意使用transform前要先分配内存。
std::transform(nums.begin(),nums.end(), ans.begin(), [](int x){return to_string(x);});
//std::sort(ans.begin(), ans.end(), [](const string &a, const string &b){return a + b > b + a;});
std::sort(ans.begin(), ans.end(), comp);
return std::accumulate(ans.begin(), ans.end(), string());
}
至少有K个重复字符的最长子串
Leetcode题目 : 找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。
//一句话概括思路:数量不足k个的那种字符不会包含在符合题意的子串中,那么这些字符可以作为分割点。
int longestSubstring(string s, int k) {
int n = s.size();
/
// 第一步:将数量不足k个的字符定为字符串分割点,记录这些分割点的下标,计入split数组
unordered_map<char, int> ump;
for(char &c : s) ump[c]++;
vector<int> split;
for(int i = 0; i < n; i++) {
if(ump[s[i]]<k) split.push_back(i);
}
// 第二步: 设置触发 递归结束条件
if(!split.size()) return n; // 递归 触发结束条件,即当前字符串中没有数量小于k的字符
split.push_back(n); // 分割字符串,将字符串的下一个点也作为分割点
int left = 0; // 记录子串起始点
int len; // 记录子串长度
int ans = 0;
// 第三步: 递归每个子串
for(int i = 0; i < split.size(); ++i) {
// 注意分割点的字符不能算入子串,所以 len 的计算不能加1
len = split[i] - left;
// 如果当前子串比已有答案还小,那就不用考虑了。
if(ans<len) ans = max(ans, longestSubstring(s.substr(left, len), k));
left = split[i] + 1; // 更新子串起始点。
}
return ans;
}
解码( 数字解码成字母的可能情况数量)
- 数字 -> 字母 Leetcode题目
输入:s = “12”
输出:2
解释:它可以解码为 “AB”(1 2)或者 “L”(12)
int numDecodings(string s) {
if (s[0] == '0') return 0;
int pre = 1, curr = 1;//dp[-1] = dp[0] = 1
// pre 表示 s[0:i-2]的解码数量
// curr 当前的值表示 s[0:i-1] 的解码数量
// 根据 pre 和前一个 curr 计算 当前 i 对应的 curr
for (int i = 1; i < s.size(); i++) {
int tmp = curr;
// curr 此时的值表示 s[0:i-1] 的解码数量
if (s[i] == '0')
if (s[i - 1] == '1' || s[i - 1] == '2') curr = pre;
else return 0;
else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6'))
curr = curr + pre;
// curr 此时的值表示 s[0:i] 的解码数量
pre = tmp;
}
return curr;
}
罗马数字与整数互转
int romanToInt(string s) {
map<char, int> romanhash = {
{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
};
int result = 0;
for(int i = 0; i < s.size() - 1; i++){
if(romanhash[s[i]] < romanhash[s[i+1]]){
result -= romanhash[s[i]];
}else{
result += romanhash[s[i]];
}
}
result += romanhash[s[s.size()-1]];
return result;
}
string intToRoman(int num) {
int values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string reps[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
string res;
for (int i = 0; i < 13; i ++ ) //这里不使用图里的count了,一遍一遍来就行了
while(num >= values[i])
{
num -= values[i];
res += reps[i];
}
return res;
}
二分查找(求左右边界)
数字在升序数组中出现的次数 牛客网题目
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if(data.empty()) return 0;
int lf = getleft(data, k);
if(data[lf] != k) return 0; //如果左边界都找不到,直接返回0
int rg = getright(data, k);
return rg-lf+1;
}
int getleft(vector<int> data, int k) {
int n = data.size();
int l = 0, r = n;
// 分为[l,mid); mid; [mid,r) 三个区域,注意是前闭后开区
while(l<r) {
int mid = l + (r-l)/2;// 防止整数溢出,比(l+r)/2要好
// 注意 求左边界 是应这样
if (data[mid] >= k) r = mid;
else l = mid+1;
}
return l; // 注意求左边界是返回 l,
}
int getright(vector<int> data, int k) {
int n = data.size();
int l = 0, r = n;
// 分为[l,mid); mid; [mid,r) 三个区域,注意是前闭后开区
while(l<r) {
int mid = l + (r-l)/2;
// 注意 求右边界 是应这样
if (data[mid] <= k) l = mid+1;
else r = mid;
}
return l-1; //注意求右边界是返回l-1
}
};
不用加减乘除做加法
牛客网题目 : 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
- 位运算
- 两个数异或:相当于每一位相加,而不考虑进位;
- 两个数相与,并左移一位:相当于求得进位;
- 将上述两步的结果相加
int Add(int num1, int num2) {
while(num2) { // 结束条件为 进位等于0
int sum = num1^num2;
int carry = (num1&num2)<<1;
num1 = sum;
num2 = carry;
}
return num1;
}
表示数值的字符串
Leetcode 题目 :请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
- 分析:用四个变量区分状态:hasNum,hasOp,hasE,hasDot。代码结构如下:
- while循环1号–略去前置空格
- while循环2号–逐字符操作
0-9 hasNum置1
e/E hasE置1,E前应该hasNum,后面是新的数字,其他三项置0
+/- hasOp置1,+/-应该出现在数字首部
. hasDot置1,点不能出现在e/E之后
空格 break
other 不应该含有其他字符 - while循环3号–略去后置空格
- 判断hasNum并且下标移动到了字符串末尾
class Solution {
public:
bool isNumber(string s) {
int n = s.size();
int index = -1;
bool hasDot = false,hasE = false,hasOp = false,hasNum = false;
while(index<n && s[++index]==' ');
while(index<n){
if('0'<=s[index] && s[index]<='9'){
hasNum = true;
}else if(s[index]=='e' || s[index]=='E'){
if(hasE || !hasNum) return false;
hasE = true;
hasOp = false;hasDot = false;hasNum = false;
}else if(s[index]=='+' || s[index]=='-'){
if(hasOp || hasNum || hasDot) return false;
hasOp = true;
}else if(s[index]=='.'){
if(hasDot || hasE) return false;
hasDot = true;
}else if(s[index]==' '){
break;
}else{
return false;
}
++index;
}
while(index<n && s[++index]==' ');
return hasNum && index==n;
}
};
数据流中的中位数
Leetcode题目 : 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
解析:
class MedianFinder {
public:
/** initialize your data structure here. */
priority_queue<int, vector<int>, less<int> > maxheap;
priority_queue<int, vector<int>, greater<int> > minheap;
MedianFinder() {}
void addNum(int num) {
if(maxheap.size() == minheap.size()) {
maxheap.push(num);
minheap.push(maxheap.top());
maxheap.pop();
}
else {
minheap.push(num);
maxheap.push(minheap.top());
minheap.pop();
}
}
double findMedian() {
int maxSize = maxheap.size(), minSize = minheap.size();
int mid1 = maxheap.top(), mid2 = minheap.top();
return maxSize == minSize ? ((mid1 + mid2) * 0.5) : mid2;
}
};
滑动窗口的最大值
Leetcode题目 : 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3;输出: [3,3,5,5,6,7]
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res; // 用来存结果的vector
deque<int> tem; // 存放窗口中元素的下标值
for(int i=0; i<nums.size(); ++i) {
//while这步操作是为了让当前窗口最大值的索引值放到tem的队头
while(!tem.empty() && nums[i]>nums[tem.back()]) {
tem.pop_back();
}
//这里的if判断条件中的第二个是为了判断队头是否过期,也就是说队头的索引是否小于当前索引
//往左走k步,+1是因为索引是从0开始,若当前滑窗索引便把它清理掉。
if(!tem.empty() && tem.front()<i-k+1) tem.pop_front();
tem.push_back(i);
if(i>=k-1) res.push_back(nums[tem.front()]);
}
return res;
}
剪绳子(绳子所能拆分的最大乘积)
Leetcode : 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
int cuttingRope(int n) {
//如果n<=3, 要求至少分为两部分,实际结果的最大值为n-1
if(n <= 3) return n-1;
vector<int> dp(n+1);
//当n大于3时, <=3的部分可以不继续拆分,它本身的长度作为dp值
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
// n > 3 时, dp[i] 表示 长度为 i 的绳子所能拆分的最大乘积
for(int i = 4; i <= n; ++i)
// 将 i 拆分成 j 和 i-j 两段
for(int j = 0; j <= i/2; ++j)
dp[i] = max(dp[i], dp[j] * dp[i-j]);
return dp[n];
}
x 的平方根
Leetcode题目 : 实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
int mySqrt(int x) {
// 二分查找
if(!x) return 0;
int l = 0, r = x, ans = -1;
while(l<=r) {
int mid = l+(r-l)/2;
if((long)mid*mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid-1;
}
}
return ans;
}
旋转数组的最小数字
Leetcode题目 :升序数组 [1,2,3,4,5]的旋转数组为 [3,4,5,1,2]。 找到这数组的最小值。以下是二分查找。
int minArray(vector<int>& numbers) {
int l = 0, r = numbers.size()-1;
while (l<r) {
int mid = l + (r-l)/2;
if (numbers[mid] > numbers[r]) {
l = mid+1;
} else if(numbers[mid] < numbers[r]) {
r = mid;
} else if(numbers[mid] == numbers[r]) {
r--;
}
}
return numbers[l];
}