1.蛇形矩阵查找
第一行递增,第二行递减,第三行递增…且每行元素都比上一行元素大
pair<int, int> search(vector<vector<int> > nums, int tar)
{
int m = nums.size(); if (m == 0) return { -1,-1 };
int n = nums[0].size(); if (n == 0) return { -1,-1 };
int left = 0;
int right = m * n - 1;
while (left <= right)
{
int mid = (left + right) / 2;
int row = mid / n;
int col = mid % n;
if (row % 2 == 1) col = n - col - 1;
int midnum = nums[row][col];
if (midnum == tar) return { row,col };
else if (midnum < tar)
left = mid + 1;
else
right = mid - 1;
}
return { -1,-1 };
}
2.合并k个有序数组
时间复杂度:O(m*log k) m是所有元素的个数
思路:
1 构造一个大小为k的小顶堆,首先先把每个元素的最小值放入堆中。
2 用优先队列实现小顶堆,节点记录每个元素所在的数组和其下标值。
3 将堆顶元素放入结果数组,每次放入后从该元素对应的数组内取一个元素放入堆中,调整这个堆。如果拿完了元素,则放入一个int_max
#include <iostream>
#include<vector>
#include<queue>
#include<limits.h>
using namespace std;
struct node {
int value;
int out_index;//行
int in_index;//列
node(int v, int o, int i) {
value = v;
out_index = o;
in_index = i;
}
//从小到大排列
bool operator<(const node &a)const {
return value > a.value;
}
};
vector<int> mergeSort(vector<vector<int>> &nums,int counts) {
vector<int> res;
priority_queue <node> order;
int N = nums.size();
//将每个数组的第一个元素(最小)放入小顶堆
for (int k = 0; k < N; k++) {
order.push(node(nums[k][0], k, 0));
}
int i = 0, j = 0;
while (res.size() < counts) {
node tmp = order.top();
res.push_back(tmp.value);
i = tmp.out_index;
j = tmp.in_index + 1;
order.pop();
if (j == nums[i].size())//达到某数组末尾
order.push(node(INT_MAX, i, j));
else
order.push(node(nums[i][j], i, j));
}
return res;
}
int main(void) {
vector<vector<int> > mat = {
{2,3,5,6},{1,4,8},{9,10,11,12}
};
vector<int> res = mergeSort(mat,11);
for (auto x : res)
cout << x << " ";
return 0;
}
leetcode 23 合并K个升序链表 同样思路
3.归并排序非递归
//合并的函数不用改
void Merge(vector<int> &a, int l, int r)
{
if (l >= r) return;
//把两个有序的段合并起来
int mid = (l + r) / 2 + 1;
vector<int> temp;
int left = l;
int right = r;
while (left <= (l + r) / 2 && mid <= right)
{
if (a[left] < a[mid])
{
temp.push_back(a[left]);
left++;
}
else
{
temp.push_back(a[mid]);
mid++;
}
}
while (left <= (l + r) / 2)
{
temp.push_back(a[left++]);
}
while (mid <= right)
{
temp.push_back(a[mid++]);
}
for (int i = 0; i <= (r - l); i++)
a[l + i] = temp[i];
}
//数组中相邻元素两两配对,用merge对它排序,然后再将相邻的两个有序数组排序.....
void MergeSort(vector<int>& data) {
int i = 1;
while (i < data.size()) {
for (int begin = 0; begin < data.size(); begin += 2 * i) {
//防止越界
int end = begin + 2 * i - 1 > data.size() - 1 ? data.size() - 1:begin + 2 * i - 1 ;
Merge(data, begin, end);
}
i = 2 * i;
}
}
4.快速排序非递归
void qsort(int a[], int l, int r)
{
stack<int> s;
s.push(r);
s.push(l);
int lwalker, rwalker, mid;
while (!s.empty())
{
int left = s.top(); s.pop();
int right = s.top(); s.pop();
lwalker = left;
rwalker = right;
mid = a[(lwalker + rwalker) / 2];
while (lwalker < rwalker)
{
while (a[lwalker] < mid) lwalker++;
while (a[rwalker] > mid) rwalker--;
if (lwalker <= rwalker)
{
int tmp = a[lwalker];
a[lwalker] = a[rwalker];
a[rwalker] = tmp;
lwalker++;
rwalker--;
}
}
if (lwalker < right)
{
s.push(right);
s.push(lwalker);
}
if (rwalker > left)
{
s.push(rwalker);
s.push(left);
}
}
}
5.编辑距离
两个字符串,可以对字符串增、删、替,求将一个字符串转到另一个最少的操作步数
动态规划,dp[i][j]代表str1的前i个到str2的前j个需要最少的步骤数。
dp[i-1][j-1]:修改str1的末尾字符
dp[i-1][j]:str1增加一个末尾字符
dp[i][j-1]:str2增加一个末尾字符,等于str1删除一个字符
如果第i个和第j个字符相同,那么:
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+1
如果第i个和第j个相同,那么:
dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1])+1)
class Solution {
public:
int minDistance(string word1, string word2)
{
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i < dp.size(); i++)
{
dp[i][0] = i;
}
for (int j = 0; j < dp[0].size(); j++)
{
dp[0][j] = j;
}
for (int i = 1; i < dp.size(); i++)
{
for (int j = 1; j < dp[i].size(); j++)
{
if (word1[i - 1] == word2[j - 1])
{
dp[i][j] = min( min(dp[i - 1][j], dp[i][j - 1])+1, dp[i - 1][j - 1]);
}
else
dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
}
}
return dp.back().back();
}
};
6 查找数组内元素
leetcode 34:在一个递增数组中查找,查找的内容是目标值开始和结束的位置。直接用二分查找即可
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.size()==0) return {-1,-1};
int left=0;
int right=nums.size()-1;
if(target<nums[0]||target>nums[right]) return {-1,-1};
while(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]==target)
{
int l=mid;
int r=mid;
while(l>=0&&nums[l]==target)
l--;
while(r<nums.size()&&nums[r]==target)
r++;
return {l+1,r-1};
}
if(nums[mid]<target)
left=mid+1;
else
right=mid-1;
}
return {-1,-1};
}
};
leetcode 33:在一个递增的数组,在中间某个位置数组被旋转,也就是前面递增的数组和后半部分递增的数组且前面数组的数都比后面大。搜索目标值。
class Solution {
public:
int search(vector<int>& nums, int target) {
int n=nums.size();
if(n==0) return -1;
if(n==1) return nums[0]==target?0:-1;
int left=0;int right=n-1;
while(left<=right)
{
int mid=(right+left)/2;
if(nums[mid]==target)
return mid;
if(nums[left]<=nums[mid])
{
if(target>=nums[left]&&target<nums[mid])
right=mid-1;
else
left=mid+1;
}
else
{
if(nums[mid]<target&&nums[right]>=target)
left=mid+1;
else
right=mid-1;
}
}
return -1;
}
};
7 二叉搜索树的节点更新为大于等于它的值的和。
class Solution {
public:
TreeNode* convertBST(TreeNode* root) {
int sum=0;
uninorder(root,sum);
return root;
}
void uninorder(TreeNode* root,int& sum)
{
if(root==NULL) return;
if(root->right) uninorder(root->right,sum);
sum+=root->val;
root->val=sum;
if(root->left) uninorder(root->left,sum);
}
};
8 反转字符串中的单词
leetcode 151 反转字符串里单词的顺序,并且把多余的空格删除
class Solution {
public:
string reverseWords(string s) {
reverse(s.begin(), s.end());
int n = s.size();
int idx = 0;//index记录的是去除掉长空格后字符串的下标
for (int start = 0; start < n; ++start) {
if (s[start] != ' ') {
if (idx != 0) s[idx++] = ' ';
int end = start;
while (end < n && s[end] != ' ') s[idx++] = s[end++];
reverse(s.begin() + idx - (end - start), s.begin() + idx);
start = end;
}
}
s.erase(s.begin() + idx, s.end());
return s;
}
};
541 对字符串s,每隔2k个字符,反转其前k个字符
557 对一个字符串,将每个单词翻转,顺序不变,保留空格
9 字符串分割split
按照字符分割和按照字符串分割
#include<vector>
#include<iostream>
#include<string>
using namespace std;
vector<string> spreststr(const string& str, char tag)
{
vector<string> res;
string subStr;
for (int i = 0; i < str.length(); i++)
{
if (tag == str[i]) //完成一次切割
{
if (!subStr.empty())
{
res.push_back(subStr);
subStr.clear();
}
}
else //将i位置的字符放入子串
{
subStr.push_back(str[i]);
}
}
if (!subStr.empty()) //剩余的子串作为最后的子字符串
{
res.push_back(subStr);
}
return res;
}
vector<string> spreststr_v2(const std::string& str, const std::string& pattern)
{
vector<string> res;
string subStr;
string temp;
int patternLen = pattern.length();
int strLen = str.length();
for (int i = 0; i < str.length(); i++)
{
if (pattern[0] == str[i] && ((strLen - i) >= patternLen))
{
temp = str.substr(i, patternLen);
if (temp == pattern)//找到一个匹配的pattern,完成切割
{
i += patternLen - 1;
if (!subStr.empty())
{
res.push_back(subStr);
subStr.clear();
}
}
else
{
subStr.push_back(str[i]);
}
}
else
{
subStr.push_back(str[i]);
}
}
if (!subStr.empty())
{
res.push_back(subStr);
}
return res;
}
int main()
{
vector<int> a={ 0,6,4,2,8,1,5,3,7,9 };
//quicksort(a, 0, 9);
string test = "i am a student andkkhe is a bot";
vector<string> res = spreststr(test,' ');
for (auto x:res)
cout<<x<< endl;
return 0;
}
10 字符串中不同的子序列
dp[i][j] 代表子序列T中前i个字符 由字符串S 前j个字符 最多能组成序列的个数
当s[j] = t[i] :dp[i][j]=dp[i-1][j-1](代表取了当前字符,可以少匹配一个子序列的字符了)+dp[i][j-1](代表没取当前字符)
当s[j] != t[i] :dp[i][j]=dp[i][j-1](代表没取当前字符,s的前j-1个能匹配多少就是多少)
int numDistinct(string s, string t) {
int s_size = s.size(), t_size = t.size();
vector<vector<long long>> dp(t_size + 1, vector<long>(s_size + 1, 0));
for(int j=0;j<=s_size;j++)
dp[0][j]=1;
//空串是所有的子序列
for (int i = 1; i <=t_size; ++i) {
for (int j = 1;j <=s_size; ++j) {
if (t[i-1] == s[j-1]) dp[i][j] = dp[i][j-1] + dp[i-1][j-1];
else dp[i][j] = dp[i][j-1];
}
}
return dp[t_size][s_size];
}
滚动数组优化:
int numDistinct(string s, string t) {
int s_size = s.size(), t_size = t.size();
vector<long long> dp(s_size + 1, 1);
for (auto c : t) {
auto last = dp[0]; // 记录上一个值
dp[0] = 0;
for (int j = 1; j <=s_size; ++j) {
auto record = dp[j];
if (s[j-1] == c) dp[j] = last + dp[j-1];
else dp[j] = dp[j-1];
last = record;
}
}
return dp.back();
}
11 二叉树中序遍历的下一个节点
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
//先排除特殊情况
if(pNode==NULL) return NULL;
//新建一个用来输出的节点
TreeLinkNode* res;
//如果当前节点有右子树则右子树的最左元素时输出
if(pNode->right)
{
res=pNode->right;
while(res->left)
res=res->left;
}
else
{
//若无右子树且无父亲节点 该节点为根,且下一个为NULL
if(pNode->next==NULL)
res=NULL;
else
{ //若有父亲节点且是父亲节点的左子节点 则父亲节点是下一个
if(pNode->next->left==pNode)
res=pNode->next;
else
{
//找到一个祖先节点,使得该点是祖先节点的左孩子的最右边的节点
TreeLinkNode* temp=NULL;
res=pNode->next;//父节点
if(res->next)
{
temp=res->next;//祖父节点
while(temp&&temp->right==res)//如果是右子树则向上找
{
res=temp;
temp=temp->next;
}
res=temp;
}
else
res=NULL;
}
}
}
return res;
}