1.找重复数:
长度n的数组,有0~n-1的数字;
第一次遇到数字 xx 时,将其交换至索引 xx 处;而当第二次遇到数字 xx 时,一定有 nums[x] = xnums[x]=x ,此时即可得到一组重复数字。
每个数字都有自己的位置 num[i]=i ,位置不正确就换位置num[i]=num[num[i]]
for(int i=0;i<numbers.size();i++)
{
if(numbers[i]==i)
continue;
else{
if(numbers[i]==numbers[numbers[i]])
return numbers[i];
else
swap(numbers[i],numbers[numbers[i]]);
//int temp=nums[i];
//nums[i]=nums[nums[i]];
//nums[temp]=temp;
}
}
return -1;
}
2.有序二维数组找到目标:
先检查数组是否合法;
数组是有序的,需要有目标的移动、查找目标,
目标过大,往右查找;目标过小,往上查找
int n= array.size();
int m=array[0].size();
if(n==0) { return false; }
if(m==0) { return false;}
for(int i=n-1, j=0;i>=0&&j<m;)
{
if(target>array[i][j])
{ j++; }
else if (target<array[i][j])
{ i--; }
else
{return true; }
}
return false;
3.从string中找到空格,然后替换“123”:
string add_s="123";
生成空白string,用加法来承接元素;
判断string里的char是不是空格;string[i]==' '
int len=s.size();
string ret="";
string add_s="%20";
for(int i=0;i<len;i++)
{
if(s[i]!=' ') {
ret=ret+s[i];
}
else{
ret=ret+add_s;
}
}
return ret;
4.反转链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
先反转链表,再插入到vector
vector<int> reversePrint(ListNode* head) {
vector<int>v;
// 空链表
if (head == nullptr) return v;
//先反转链表
ListNode* CURR=head->next;
ListNode* temp;
head->next=nullptr;//头节点当作尾
while(CURR){
//反转
temp=CURR->next;
CURR->next=head;
//为下一次循环做准备
head=CURR;
CURR=temp;
}
// 取出链表中的值
while(head){
v.push_back(head->val);
head=head->next;
}
return v;
}
5.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
二叉树前序遍历的顺序为:
先遍历根节点;随后递归地遍历左子树;最后递归地遍历右子树。
二叉树中序遍历的顺序为:
先递归地遍历左子树;随后遍历根节点;最后递归地遍历右子树
class Solution {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder,
int pre_left,int pre_right,int in_left,int in_right){
if(pre_left>pre_right){ return nullptr;}
//前序第一个是根节点
int pre_root=pre_left;//pre_root--preorder 根节点坐标位置
int in_root=index[preorder[pre_root]];//in_root--inorder 根节点坐标位置
//int in_root=index[preorder[pre_left]];
//根节点建立
TreeNode* root=new TreeNode(preorder[pre_root]);
//TreeNode* root=new TreeNode(preorder[pre_left]);
//左树节点数目
int size_left_tree=in_root-in_left;
//构建左树
root->left=myBuildTree(preorder,inorder, pre_left+1,pre_left+size_left_tree,
in_left,in_root-1);
//构建右树
root->right=myBuildTree(preorder,inorder, pre_left+size_left_tree+1,pre_right,
in_root+1,in_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n=preorder.size();
//哈希映射 定位根节点
for(int i=0;i<n;i++){
index[inorder[i]]=i;
}
return myBuildTree(preorder,inorder, 0,n-1, 0,n-1);
}
};
6.用两个栈实现一个队列
完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead
操作返回 -1 )
两个独立的栈,一个存储尾插,一个删除输出
队列是先进先出,等第n次的删除输出栈为空,再把存储栈的数据放入
class CQueue {
public:
CQueue() {}
//尾插
void appendTail(int value) {
this->in_data.push(value);
}
//删头
int deleteHead() {
//重点:只有输出栈为空才把存储栈的数据放进来
if(this->out_data.empty()) {
if(this->in_data.empty()) {
return -1;
}
this->in_out();//后面存储栈已经清空
}
int value=this->out_data.top();
this->out_data.pop();
return value;
}
private:
//正序
stack<int> in_data;
//倒序
stack<int> out_data;
//两个栈的顺序不一样 模拟队列的进、出
void in_out() {
while(!in_data.empty()) {
out_data.push(in_data.top());//把存储栈的内容倒置
in_data.pop();//清空存储栈
}
}
};
7.输入 n
,求斐波那契(Fibonacci)数列的第 n
项(即 F(N)
)。
不需要保存所有的值,所以只保留 加数、被加数 即可
class Solution {
public:
int fib(int n) {
if(n<2){return val[n];}
else{
for(int i=2;i<=n;i++){
int temp=val[1];
val[1]=(val[1]+val[0])%mod_mun;
val[0]=temp;
}
return val[1];
}
}
private:
int mod_mun=1000000007;
vector<int> val={0,1};
};
8.一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n
级的台阶总共有多少种跳法
class Solution {
public:
int numWays(int n) {
int fun_num;
if(n<=1){
return 1;
}
else{
//不需要单独考虑n=2的情况
for(int i=3;i<=n;i++){
int temp=v1[0];
v1[0]=v1[1];
v1[1]=(temp+v1[1])%mod_num;
}
return v1[1];
}
}
private:
int mod_num=1000000007;
vector<int> v1={1,2};
};
9.旋转数组最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。给你一个可能存在重复元素值的数组 numbers ,它原来是一个 升序 排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。
旋转之后的数组一个升序数组分成两个数组 之后的拼接;由于1.顺序存储 2.元素有序 所以用二分法查找交界点,最小值在交界点的右边
class Solution {
public:
int minArray(vector<int>& numbers) {
int left=0;
int right=numbers.size()-1;
while(left<right){
int mid=(left+right)/2;
//中间比最右边大,说明左半部分是有序的,分界点在右半部分
//且mid位置不是最小值
if(numbers[mid]>numbers[right]){
left=mid+1;
}
//中间比最右边小,说明右半部分是有序的,说明分界点在左半部分
else if(numbers[mid]<numbers[right]){
right=mid;
}
//中间、最右边相等,不能判断分界点位置,所以缩小查找范围
else{
right=right-1;
}
}
return numbers[left];
}
};
10.矩阵中的路径
一个二维字符网格和一个字符串单词 word ;如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。
同一个单元格内的字母不允许被重复使用。
需要用到递归,先找到一个起始点,然后一直递归下去。
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
int row=board.size();//行
int col=board[0].size();//列
//两个for循环是为了寻找 开始的位置
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
if(this->fun(board, word,i,j,0)){
return true;
}
}
}
//没有正确的路径
return false;
}
private:
//寻找的路径
bool fun(vector<vector<char>>& board, string word,int i,int j,int k){
int row=board.size();//行
int col=board[0].size();//列
//int i 寻找的行数
//int j 寻找的列数
//int k word的第k个字符
//路径超出查找范围 或者 查找的字符不对
if(i<0||i>=row||j<0||j>=col||board[i][j]!=word[k]){return false;}
//k=word.length() 就是已经查找完毕,找到路径
if(k==word.length()-1){return true;}
//查找到当前需要的字符
//先把当前位置置空,防止后面递归的时候重复使用
board[i][j]=' ';
//沿着当前位置的上下左右寻找
//沿着下面的路径走下去,后面的board[i+-1][j+-1]也会置空
bool ret=fun(board, word,i-1,j,k+1)||fun(board, word,i+1,j,k+1)||
fun(board, word,i,j-1,k+1)||fun(board, word,i,j+1,k+1);
//把原先的数据恢复
board[i][j]=word[k];
return ret;
}
};
11.剪绳子
一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子长度可能的最大乘积
把绳子先切一刀分成 j 和 i-j ,j 部分不切分,i-j 部分找到最大乘积
遍历 j,找到起始切分位置
class Solution {
public:
int cuttingRope(int n) {
//0 不是正整数,1 是最小的正整数,0 和 1 都不能拆分,因此 dp[0]=dp[1]=0
vector<int> dp(n+1);
dp[2]=1;
//求长度为i的绳子的最大乘积
for(int i=3;i<=n;i++){
//第一刀切分成j和i-j两部分;
//j部分不再切分
for(int j=1;j<i-1;j++){
//后面的dp[i]是上一次的j得到的最大乘积
dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]));
}
}
return dp[n];
}
};
12.调整数组顺序 奇数在前,偶数在后
求奇偶,可以%2,也可以&1(偶数为0.奇数为1)
class Solution {
public:
vector<int> exchange(vector<int>& nums)
{
int left=0;
int right=nums.size()-1;
while(left<right){
while(left<right && (nums[left] & 1)==1)//为奇数
{
left++;
}
while(left<right && (nums[right] & 1)==0)//为偶数
{
right--;
}
swap(nums[left],nums[right]);
}
return nums;
}
};
13.从链表倒数第k个节点输出
a、单指针 需要计算list长度
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode *node=nullptr;
int i=0;
//算长度
for(node=head;node!=nullptr;node=node->next){
i++;
}
//i>k 卡到倒数第k个
for(node=head;i>k;i--){
node=node->next;
}
return node;
}
};
b、双指针 让两个指针间隔k个距离
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode *p1=head;
ListNode *p2=head;
for(int i=0;i<k;i++){
p1=p1->next;
}
//让两个指针间隔k个距离 p1到尾,p2到倒数k
while(p1){
p1=p1->next;
p2=p2->next;
}
return p2;
}
};