目录
一、数组
1、数组中重复的数字
2、二维数组的查找
3、数字在排序数组中出现的次数
4、旋转数组的最小数字
5、调整数组顺序使奇数位于偶数前面
6、顺指针打印矩阵
7、数组中出现次数超过一半的数字
8、连续子数组的最大和
9、数组中的逆数对
10、数组中只出现过一次的数字
11、把数组排成最小的数
12、构建乘积数组
二、链表
1、从尾到头打印链表
2、链表中倒数第K个节点
3、反转链表
4、合并两个排序链表
5、两个链表的第一个公共节点
6、链表中环的入口节点
7、删除链表中重复的节点
8、复杂链表的复制
三、字符串
1、替换空格
2、字符串的排列
3、第一个只出现一次的字符
4、左旋转字符串
5、反转单词序列
6、把字符串转换成整数
7、正则表达式匹配
8、表示数值的字符串
9、字符流中第一个不重复的字符
四、栈和队列
1、用两个栈实现一个队列
2、包含main函数的栈
3、栈的压入弹出序列
五、树和二叉树
1、重建二叉树
2、树的子结构
3、二叉树镜像
4、从上往下打印二叉树
5、二叉搜索树的后序遍历序列
6、二叉树中和为某一值的路径
7、二叉搜索树与双向链表
8、二叉树深度
9、平衡二叉树
10、二叉树的下一个结点
11、对称的二叉树
12、按之字形顺序打印二叉树
13、序列化二叉树
14、把二叉树打印成多行
15、二叉搜索树的第K个节点
六、动态规划
1、斐波那契数列
2、跳台阶
3、变态跳台阶
4、矩形覆盖
5、剪绳子
七、回溯法
1、矩阵中的路径
2、机器人运动范围
八、数学
1、数值的整数次方
2、求1+2+3+……+n
3、不用加减乘除做加法
4、二进制中1的个数
九、其他
1、最小的K个数
2、从1到n整数中1出现的次数
3、丑数
4、和为S的连续正数序列
5、和为S的两个数字
6、扑克牌顺子
7、圆圈中最后剩下的数
8、数据流中的中位数
9、滑动窗口的最大值
题目:
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
LeetCode链接:力扣
思路:
示例代码:
int findRepeatNumber(vector& nums) {
vector vecTemp(nums.size(),0);
for (auto &num : nums)
{
vecTemp[num]++;
}
for(auto i = 0; i < vecTemp.size() ;i++)
{
if (vecTemp[i] > 1)
{
return i;
}
}
return -1;
}
题目:
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。给定 target = 20,返回 false。
LeetCode链接:力扣
思路:
三种方法
示例代码:
//直接遍历
bool findNumberIn2DArray(vector>& matrix, int target) {
if (matrix.size() == 0 || matrix[0].size() == 0)
{
return false;
}
int n = matrix.size();
int m = matrix.at(0).size();
for(int i = 0 ;i < n;i++)
{
for (int j = 0;j < m;j++)
{
if (matrix[i][j] == target)
{
return true;
}
}
}
return false;
}
//遍历优化
bool findNumberIn2DArray(vector>& matrix, int target) {
if (matrix.size() == 0 || matrix[0].size() == 0)
{
return false;
}
int n = matrix.size();
int m = matrix.at(0).size();
for(int i = 0 ;i < n;i++)
{
for (int j = 0;j < m;j++)
{
if (matrix[i][j] == target)
{
return true;
}
if (matrix[i][j] > target)
{
m = j;//将m-j后面的列去掉
}
}
}
return false;
}
//大于目标cal--,小于row++
bool findNumberIn2DArray(vector>& matrix, int target) {
if (matrix.size() == 0 || matrix[0].size() == 0)
{
return false;
}
int n = 0;
int m = matrix.at(0).size()-1;
while(n < matrix.size() && m >= 0)
{
if (matrix[n][m] == target)
{
return true;
}
else if (matrix[n][m] > target)
{
m--;
}
else if (matrix[n][m] < target)
{
n++;
}
}
return false;
}
题目:
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
LeetCode链接:力扣
思路:
两种方法:
示例代码:
//直接遍历
int search(vector& nums, int target) {
int nCount = 0;
for (auto &num : nums)
{
if (num == target)
{
nCount++;
}
}
return nCount;
}
//二分查找
int search(vector& nums, int target) {
int nRet = 0;
int left = 0;
int right = nums.size()-1;
while (left <= right)
{
int mid = left + (right - left)/2;
if (nums[mid] == target)//找到
{
nRet++;
int nIndex = mid-1;
while(nIndex >= 0 &&nums[nIndex] == target )//向左查找
{
nRet++;
nIndex--;
}
nIndex = mid + 1;
while(nIndex < nums.size() && nums[nIndex] == target)//向右查找
{
nRet++;
nIndex++;
}
break;
}
else if (nums[mid] > target)
{
right = mid-1;
}
else
{
left = mid+1;
}
}
return nRet;
}
题目:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:输入:[2,2,2,0,1]
输出:0
Leecode链接:力扣
思路:
两种方式:
示例代码:
//二分法
int minArray(vector& numbers) {
if (numbers.size() == 0)
{
return -65535;
}
int nLeft = 0;
int nRight = numbers.size()-1;
while(nLeft < nRight)
{
int nMid = nLeft + (nRight - nLeft)/2;
if ( numbers[nMid] < numbers[nRight])//右侧有序,缩小区间
{
nRight = nMid;
}
else if (numbers[nMid] == numbers[nRight])//特殊情况,一个一个比对
{
nRight--;
}
else //左侧有序,缩小区间
{
nLeft = nMid+1;
}
//std::cout << nLeft << nRight << nMid <& numbers) {
if (numbers.size() == 0)
{
return -65535;
}
for(int i = 0; i < numbers.size()-1; ++i){
if( numbers[i] > numbers[i + 1] )
{
return numbers[i+1];
}
}
return numbers[nLeft];
}
题目:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
示例:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
Leecode链接:力扣
思路:
示例代码
vector exchange(vector& nums) {
int left = 0;
int right = nums.size() - 1;
while(left <= right)
{
if (nums[left]%2==1)
{
left++;
}
else
{
int nTemp = nums[right];
nums[right] = nums[left];
nums[left] = nTemp;
right--;
}
}
return nums;
}
题目:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
Leecode链接:力扣
思路:
示例代码:
vector spiralOrder(vector>& matrix) {
if (matrix.size() == 0)
{
return vector ();
}
vector ret;
int top = 0;
int bottom = 0;
int nRow = matrix.size()-1;
int nCol = matrix.at(0).size()-1;
while (top <= nRow && bottom <= nCol)
{
for (int i = bottom; i < nCol;i++)//从左到右
{
ret.push_back(matrix[top][i]);
}
if (++top > nRow)
{
break;
}
for (int j = top; j < nRow;j++)//从上到下
{
ret.push_back(matrix[j][nCol]);
}
if (--nCol < bottom)
{
break;
}
for (int k = nCol; k >= bottom;k--)//从右到左
{
ret.push_back(matrix[nRow][k]);
}
if (--nRow < top)
{
break;
}
for (int m = nRow; m >= top;m--)//从下到上
{
ret.push_back(matrix[m][bottom]);
}
if (++bottom > nCol)
{
break;
}
}
return ret;
}
题目:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
LeetCode链接:力扣
思路:
两种方式:
示例代码:
//排序,取中
int majorityElement(vector& nums) {
std::sort(nums.begin(),nums.end());
return nums[nums.size()/2];
}
//摩尔投票法
int majorityElement(vector& nums) {
int nCount = 0;
int nTemp = 0;
for (auto &val : nums)
{
if (nCount == 0)
{
nTemp = val;
nCount++;
}
else
{
nTemp == val ? nCount++ : nCount--;
}
}
return nTemp;
}
题目:
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
LeetCode链接:力扣
思路:
两种方法:
示例代码:
//原数组修改
int maxSubArray(vector& nums) {
if (nums.size() == 0)
{
return 0;
}
int nMax = nums[0];
int result = nMax;
for (int i = 1; i < nums.size(); i++)
{
if (nMax + nums[i] > nums[i])
{
nMax = nMax + nums[i];
}
else
{
nMax = nums[i];
}
result = max(nMax, result);
}
return result;
}
//dp思想
int maxSubArray(vector& nums) {
if (nums.size() == 0)
{
return 0;
}
int nMax = nums[0];
vector dp(nums.size(),0);
dp[0] = nMax;
for (int i = 1; i < nums.size(); i++)
{
dp[i] = max(nums[i],nums[i] + dp[i-1]);
nMax = max(nMax,dp[i]);
}
return nMax;
}
题目:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
LeetCode链接:力扣
思想:
如图:
示例代码:
int reversePairs(vector& nums) {
vector tmp(nums.size());
return mergeSort(0, nums.size() - 1, nums, tmp);
}
int mergeSort(int l, int r, vector& nums, vector& tmp) {
// 终止条件
if (l >= r) return 0;
// 递归划分
int m = (l + r) / 2;
int res = mergeSort(l, m, nums, tmp) + mergeSort(m + 1, r, nums, tmp);
// 合并阶段
int i = l, j = m + 1;
for (int k = l; k <= r; k++)
tmp[k] = nums[k];
for (int k = l; k <= r; k++) {
if (i == m + 1)
nums[k] = tmp[j++];
else if (j == r + 1 || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
else {
nums[k] = tmp[j++];
res += m - i + 1; // 统计逆序对
}
}
return res;
}
题目:
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字
Leecode链接:力扣
思路:
三种方法:
i = ret ^ (-ret)
因为计算机用补码存取二进制数,而负数的补码为反码+1,举个例子:假如ret = 1110 , -ret = 0010 , 所以 i = 0010
所以,再异或一遍即可得到答案)示例代码:
//排序,遍历
void FindNumsAppearOnce(vector data,int* num1,int *num2) {
std::sort(data.begin(),data.end());
vector tempVec;
for (int i = 0;i < data.size();i++)
{
if (i+1 < data.size() && data[i] == data[i+1])
{
i++;
continue;
}
tempVec.push_back(data[i]);
}
*num1 = tempVec[0];
*num2 = tempVec[1];
}
//哈希表,遍历
void FindNumsAppearOnce(vector data,int* num1,int *num2) {
unordered_map mp;
for (const int k : data) ++mp[k];
vector ret;
for (const int k : data) {
if (mp[k] == 1) {
ret.push_back(k);
}
}
*num1 = ret[0];
*num2 = ret[1];
}
//异或
void FindNumsAppearOnce(vector data,int* num1,int *num2) {
int ret = 0;
for (const int k : data) ret ^= k;
ret &= (-ret);
*num1 = 0, *num2 = 0;
for (const int k : data) {
if (k & ret) *num1 ^= k;
else *num2 ^= k;
}
}
题目:
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
示例 1:
输入: [10,2]
输出: "102"
示例 2:输入: [3,30,34,5,9]
输出: "3033459"
LeetCode链接:力扣
思路:
示例代码:
string minNumber(vector& nums) {
vectorvecRet;
string ans;
for(int i = 0; i < nums.size(); i ++){
vecRet.push_back(to_string(nums[i]));
}
sort(vecRet.begin(), vecRet.end(), [](string& s1, string& s2){return s1 + s2 < s2 + s1;});
for(int i = 0; i < vecRet.size(); i ++)
{
ans += vecRet[i];
}
return ans;
}
题目:
给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
示例:
输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
LeetCode链接:力扣
思想:
两种方法:
示例代码:
//暴力遍历
vector constructArr(vector& a) {
if (a.size() == 0)
{
return vector();
}
vector ret;
for (int i = 0 ;i < a.size();i++)
{
int temp = 1;
for (int j = 0; j < a.size();j++)
{
if (i == j)
{
continue;
}
temp*=a[j];
}
ret.push_back(temp);
}
return ret;
}
//上下三角
vector constructArr(vector& a) {
vector ret(a.size());
if(a.size() <= 0){
return ret;
}
ret[0] = 1;
for(int i = 1; i < a.size(); i++){
ret[i] = ret[i - 1] * a[i - 1];
}
int temp = 1;
for(int i = a.size() - 2; i >= 0; i--){
temp *= a[i + 1];
ret[i] *= temp;
}
return ret;
}
题目:
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2] 输出:[2,3,1]
LeetCode链接:力扣
思路:
示例代码:
vector reversePrint(ListNode* head) {
vector vecTemp;
while (head != nullptr)
{
vecTemp.push_back(head->val);
head = head->next;
}
return vector(vecTemp.rbegin(),vecTemp.rend());
}
题目:
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
LeetCode链接:力扣
思路:
示例代码:
ListNode* getKthFromEnd(ListNode* head, int k) {
if (head == nullptr || k < 0)
{
return head;
}
ListNode *fast= head;
ListNode *slow = head;
while(k--> 1)
{
fast = fast->next;
}
while(fast != nullptr && fast->next != nullptr)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
题目:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:输入:head = []
输出:[]
LeetCode链接:力扣
代码示例:
//就地反转
ListNode * reverse1(ListNode *node)
{
if (node == nullptr)
{
return node;
}
ListNode *head = new ListNode(-1);
head->next = node;
ListNode *curNode = node;
ListNode *nextNode = curNode->next;
while (nextNode != nullptr) {
curNode->next = nextNode->next;
nextNode->next = head->next;
head->next = nextNode;
nextNode = curNode->next;
}
return head->next;
}
//头插法
ListNode * reverse2(ListNode *node)
{
ListNode *preNode = new ListNode(-1);
ListNode *pCur = node;
while (pCur != nullptr) {
ListNode *pNext = pCur->next;
pCur->next = preNode->next;
preNode->next = pCur;
pCur = pNext;
}
return preNode->next;
}
//迭代
ListNode * reverse3(ListNode *node)
{
ListNode *prev = nullptr;
while(node != nullptr){
ListNode *tmpNode = node->next;
node->next = prev;
prev = node;
node = tmpNode;
}
return prev;
}
//递归
ListNode * reverse4(ListNode *node)
{
if(node == nullptr|| node->next == nullptr)
{
return node;
}
ListNode *prev = reverse4(node->next);
node->next->next = node;
node->next = nullptr;
return prev;
}
//创建初始化链表
ListNode * createListNode()
{
ListNode *head = nullptr;
ListNode *node1 = new ListNode(1);
ListNode *node2 = new ListNode(2);
ListNode *node3 = new ListNode(3);
ListNode *node4 = new ListNode(4);
ListNode *node5 = new ListNode(5);
head = node1;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = nullptr;
ListNode *newHead = reverse(head);
while (newHead != nullptr)
{
qDebug() << newHead->val;
newHead = newHead->next;
}
}
题目:将两个有序链表合并成一个新的链表,新链表为通过两个链表拼接成新的节点生成
输入:1->2->4,1->3->4
输出:1->1->2->3->4->4
LeetCode链接:力扣
分析:
1)如果有一个链表为空,返回另外一个链表即可
2)遍历两个链表,比较值,取出较小值地址赋给新链表,则原链表后移(list->next=list)
3)其中一个链表遍历完,另外一个链表可能还存在多余的节点,直接赋值给新链表
代码示例:
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
ListNode* mergeTwoLists(ListNode* listNode1, ListNode* listNode2) {
if (listNode1 == nullptr)//如果链表1为空,直接返回链表2
{
return listNode2;
}
if (listNode2 == nullptr)//如果链表2为空,直接返回链表1
{
return listNode1;
}
ListNode head(0);
ListNode* node = &head;
while(listNode1 != nullptr && listNode2 != nullptr)
{
if (listNode1->val > listNode2->val)//如果链表1节点大于链表2节点,将链表2节点地址赋给node,链表2后移
{
node->next = listNode2;
listNode2 = listNode2->next;
}
else //如果链表1节点小于链表2节点,将链表1节点地址赋给node,链表1后移
{
node->next = listNode1;
listNode1= listNode1->next;
}
node = node->next;
}
if (listNode1 == nullptr) //链表1遍历完,将剩余链表2赋值给node
{
node->next = listNode2;
}
if (listNode2 == nullptr)//链表2遍历完,将剩余链表1赋值给node
{
node->next = listNode1;
}
return head.next;
}
};
题目:
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
LeetCode链接:力扣
思路:
示例代码:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == nullptr || headB == nullptr)
{
return nullptr;
}
ListNode *tempA = headA;
ListNode *tempB = headB;
while(tempA != tempB)
{
tempA = tempA == nullptr ? headB:tempA->next;
tempB = tempB == nullptr ? headA:tempB->next;
}
return tempA;
}
题目:
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
LeetCode链接:力扣
思路:
示例代码:
ListNode *detectCycle(ListNode *head) {
if (head == nullptr)
{
return head;
}
ListNode *fast = head;
ListNode *slow = head;
bool bRet = false;
while(fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast)
{
bRet = true;
break;
}
}
if (!bRet)
{
return nullptr;
}
slow = head;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
题目:
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
LeetCode链接:力扣
思路:
示例代码:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == nullptr || pHead->next == nullptr) return pHead;
ListNode dummpyHead(0);
dummpyHead.next = pHead;
ListNode *pre = &dummpyHead;
ListNode *cur = dummpyHead.next;//cur是真正工作的节点
while(cur != nullptr){
if(cur->next != nullptr && cur->val == cur->next->val){
while(cur->next != nullptr && cur->val == cur->next->val)
{
cur = cur->next;
}
pre->next = cur->next;//这里还不要马上把 pre 赋值过来
cur = cur->next;
}else{
pre = pre->next;
cur = cur->next;
}
}
return dummpyHead.next;
}
题目:
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
LeetCode链接:力扣
思路:
dic
, 节点 cur
指向头节点dic
添加键值对 (原 cur 节点, 新 cur 节点)
示例代码:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
unordered_map map;
// 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
while(cur != nullptr) {
map[cur] = new Node(cur->val);
cur = cur->next;
}
cur = head;
// 4. 构建新链表的 next 和 random 指向
while(cur != nullptr) {
map[cur]->next = map[cur->next];
map[cur]->random = map[cur->random];
cur = cur->next;
}
// 5. 返回新链表的头节点
return map[head];
}
题目:请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = "We are happy."
输出:"We%20are%20happy."
LeetCode链接:力扣
思路:
示例代码:
string replaceSpace(string s) {
string strRet = "";
for(auto &c : s)
{
if (c == ' ')
{
strRet += "%20";
}
else {
strRet.push_back(c);
}
}
return strRet;
}
题目:
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
LeetCode链接:力扣
思路:
两种方法:
示例代码:
//使用c++的next_permutation函数
vector permutation(string s) {
sort(s.begin(), s.end());
vector res;
do{
res.emplace_back(s);
}while(next_permutation(s.begin(), s.end()));
return res;
}
//回溯算法
std::set setRet;
vector vecRet;
void dfs(string& s, string& cur, vector& used){
if(cur.size() == s.size()){
setRet.insert(cur);
return;
}
for(int i = 0; i < s.size(); ++i){
if(used[i]) continue;
cur += s[i];
used[i] = true;
dfs(s, cur, used);
used[i] = false;
cur.pop_back();
}
}
vector permutation(string s) {
string cur = "";
vector used(s.size(), false);
dfs(s, cur, used);
for(auto& ele : setRet) vecRet.push_back(ele);
return vecRet;
}
题目:
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例 1:
输入:s = "abaccdeff"
输出:'b'
示例 2:输入:s = ""
输出:' '
LeetCode链接:力扣
思路:
示例代码:
char firstUniqChar(string s) {
unordered_map map;
for (int i = s.size()-1;i >= 0 ;i--)
{
map[s[i]]++;
}
for (auto & val : map)
{
std::cout << val.second << val.first << std::endl;
if (val.second == 1)
{
return val.first;
}
}
return ' ';
}
题目:
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2
输出: "cdefgab"
示例 2:输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
LeetCode链接:力扣
思路:
示例代码:
string reverseLeftWords(string s, int n) {
if (n <= 0)
{
return s;
}
int nLen = s.length();
if (n >= nLen)
{
n = n%nLen;
}
s+=s;
return s.substr(n,nLen);
}
题目:
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 1:
输入: "the sky is blue"
输出: "blue is sky the"
示例 2:输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
LeetCode链接:力扣
思路:
两种方法:
示例代码:
//直接遍历
string reverseWords(string s) {
if (s.length() == 0 )
{
return s;
}
string strRet = "";
string strTemp = "";
for (auto &ch : s)
{
if (ch != ' ')
{
strTemp += ch;
}
else if (strTemp.length() != 0)//处理多个空格问题
{
strRet =" " + strTemp + strRet;
strTemp = "";
}
}
if (strTemp.size() != 0)
{
strRet = strTemp + strRet;
}
if (strRet[0] == ' ')//处理收尾空格
{
strRet = strRet.substr(1,strRet.length()-1);
}
return strRet;
}
//双指针
string reverseWords(string s) {
//nBegin,nEnd用于确定跳过首尾空格的下标范围
int nBegin = 0, nEnd = s.length()-1;
//跳过s的首部空格
while(nBegin <= nEnd&&s[nBegin]==' ') nBegin++;
//跳过s的尾部空格
while(nBegin <= nEnd&&s[nEnd]==' ') nEnd--;
//k,w为用于确定每个单词范围的双指针,从非空格尾部开始往前扫描,nBegin为前边界,nEnd为后边界
int k = nEnd, w = nEnd;
//当输入全为空格时,跳过首尾空格后nBegin > nEnd
string strRet;
while(nBegin <= nEnd && k >= nBegin){
//k往前扫描直到遇到空格停下,或者超出nBegin前边界停下
while(k >= nBegin && s[k] !=' ') k--;
//k+1到w为一个单词的范围,将每个字符按序加入string strRet即可
for(int i = k+1;i <= w; i++) strRet += s[i];
//没超出前边界nBegin时,k停下遇到的肯定是空格,可能是一个或多个,跳过
if(k >= nBegin && s[k] ==' '){
while(k >= nBegin && s[k] ==' ') k--;
//跳过一个或多个空格后,strRet加一个必要的空格
strRet += ' ';
}
//w跳到k位置继续扫描下一个单词范围
w = k;
}
return strRet;
}
题目:
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42"
输出: 42
示例 2:输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
LeetCode链接:力扣
思路:
示例代码:
int strToInt(string str) {
if (str.size() == 0 || (str[0] >= 'a' && str[0] <= 'z'))//空字符、首字符不符合
{
return 0;
}
int nBegin = 0;
while (nBegin <= str.size()-1 && str[nBegin] == ' ')nBegin++;//去除前面空格
if (nBegin >= str.size())//如果全是空格串
{
return 0;
}
int nSign = 1;
if (str[nBegin] == '-')//判断正负号
{
nSign = -1;
nBegin++;
}
else if (str[nBegin] == '+')
{
nSign = 1;
nBegin++;
}
long nRet = 0;
for (int i = nBegin; i < str.size(); i++)
{
if (str[i] < '0' || str[i] > '9')//不符合退出
{
break;
}
nRet = nRet *10 + nSign *(str[i] - '0');//累加
if (nRet > INT_MAX)//大于最大值
{
return INT_MAX;
}
if (nRet < INT_MIN)//小于最小值
{
return INT_MIN;
}
}
return nRet;
}
题目:
请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
LeetCode链接:力扣
思路:
示例代码:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
std::vector> dp (m + 1,vector(n + 1,false));
for(int i = 0;i <= m; i++){
for(int j = 0; j <= n; j++){
if(j == 0)//正则串是空串的dp初始化
{
dp[i][j] = i == 0; //如果i==0为true,否则为false
}
else if(p[j-1] != '*')
{
if(i > 0 && (s[i-1] == p[j-1] || p[j-1] =='.'))
{
dp[i][j] = dp[i-1][j-1];
}
}
else
{
if(j > 1)//不看,直接砍掉p后两个字符
{
dp[i][j] = dp[i][j] | dp[i][j-2];
}
if(i > 0 && j > 1 && (s[i-1] == p[j-2] || p[j-2] == '.'))
{
dp[i][j] = dp[i][j] | dp[i-1][j];
}
}
}
}
return dp[m][n];
}
题目:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
数值(按顺序)可以分成以下几个部分:
若干空格
一个 小数 或者 整数
(可选)一个 'e' 或 'E' ,后面跟着一个 整数
若干空格
小数(按顺序)可以分成以下几个部分:(可选)一个符号字符('+' 或 '-')
下述格式之一:
至少一位数字,后面跟着一个点 '.'
至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
一个点 '.' ,后面跟着至少一位数字
整数(按顺序)可以分成以下几个部分:(可选)一个符号字符('+' 或 '-')
至少一位数字
部分数值列举如下:["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
部分非数值列举如下:["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
示例 1:
输入:s = "0"
输出:true
示例 2:输入:s = "e"
输出:false
示例 3:输入:s = "."
输出:false
示例 4:输入:s = " .1 "
输出:true
LeetCode链接:力扣
思路:
示例代码:
bool isNumber(string s) {
//1、从首尾寻找s中不为空格首尾位置,也就是去除首尾空格
int i=s.find_first_not_of(' ');
if(i==string::npos)return false;
int j=s.find_last_not_of(' ');
s=s.substr(i,j-i+1);
if(s.empty())return false;
//2、根据e来划分底数和指数
int e=s.find('e');
//3、指数为空,判断底数
if(e==string::npos)return judgeP(s);
//4、指数不为空,判断底数和指数
else return judgeP(s.substr(0,e))&&judgeS(s.substr(e+1));
}
bool judgeP(string s)//判断底数是否合法
{
bool result=false,point=false;
int n=s.size();
for(int i=0;i'9'){//非纯数字,返回false
return false;
}
else{
result=true;
}
}
return result;
}
bool judgeS(string s)//判断指数是否合法
{
bool result=false;
//注意指数不能出现小数点,所以出现除符号位的非纯数字表示指数不合法
for(int i=0;i'9'){//非纯数字,返回false
return false;
}
else{
result=true;
}
}
return result;
}
题目:
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例 1:
输入:s = "abaccdeff"
输出:'b'
示例 2:输入:s = ""
输出:' '
LeetCode链接:力扣
思路:
示例代码:
char firstUniqChar(string s) {
unordered_map map;
for (int i = s.size()-1;i >= 0 ;i--)
{
map[s[i]]++;
}
for (auto & val : map)
{
std::cout << val.second << val.first << std::endl;
if (val.second == 1)
{
return val.first;
}
}
return ' ';
}
题目:
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
LeetCode链接:力扣
思路:
示例代码:
class CQueue {
public:
CQueue() {
}
void appendTail(int value) {
m_stack1.push(value);
}
int deleteHead() {
if (m_stack2.empty())
{
while(!m_stack1.empty())
{
m_stack2.push(m_stack1.top());
m_stack1.pop();
}
}
int n = -1;
if (!m_stack2.empty())
{
n = m_stack2.top();
m_stack2.pop();
}
return n;
}
std::stack m_stack1;
std::stack m_stack2;
};
题目:
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
LeetCode链接:力扣
思路:
示例代码:
MinStack() {
m_vec.clear();
}
void push(int x) {
m_vec.push_back(x);
}
void pop() {
m_vec.pop_back();
}
int top() {
return m_vec[m_vec.size()-1];
}
int min() {
int nMin = INT_MAX;
for (auto &n : m_vec)
{
nMin = n > nMin ? nMin : n;
}
return nMin;
}
vector m_vec;
};
题目:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
LeetCode链接:力扣
思路:
示例代码:
bool validateStackSequences(vector& pushed, vector& popped) {
stack stk;
int nIndex = 0;
for(auto &val : pushed)
{
stk.push(val);
while (!stk.empty() && stk.top() == popped[nIndex])
{
stk.pop();
nIndex++;
}
}
return stk.empty();
}
题目:
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例 2:Input: preorder = [-1], inorder = [-1]
Output: [-1]
LeetCode链接:力扣
思路:
示例代码:
TreeNode* buildTree(vector& preorder, vector& inorder) {
this->preorder = preorder;
for(int i = 0; i < inorder.size(); i++)
dic[inorder[i]] = i;
return recur(0, 0, inorder.size() - 1);
}
vector preorder;
unordered_map dic;
TreeNode* recur(int root, int left, int right) {
if(left > right) return nullptr; // 递归终止
TreeNode* node = new TreeNode(preorder[root]); // 建立根节点
int i = dic[preorder[root]]; // 划分根节点、左子树、右子树
node->left = recur(root + 1, left, i - 1); // 开启左子树递归
node->right = recur(root + i - left + 1, i + 1, right); // 开启右子树递归
return node; // 回溯返回根节点
}
题目:
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:3
/ \
4 5
/ \
1 2
给定的树 B:4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:输入:A = [3,4,5,1,2], B = [4,1]
输出:true
LeetCode链接:力扣
思路:
示例代码:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if (A == nullptr || B == nullptr)
{
return false;
}
bool bRet = false;
if (A->val == B->val)//A、B相同,继续查找
{
bRet= helpCheck(A,B);
}
if (!bRet)
{
bRet = isSubStructure(A->left,B) || isSubStructure(A->right,B);//A和B不相同,遍历左子树和右子树
}
return bRet;
}
bool helpCheck(TreeNode* A, TreeNode* B)
{
if (B == nullptr)//B遍历成功,是子树
{
return true;
}
if (A == nullptr || A->val != B->val)//A遍历完或者A不等于B值,不是子树
{
return false;
}
return helpCheck(A->left,B->left) && helpCheck(A->right,B->right);//递归遍历
}
题目:
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/ \
2 7
/ \ / \
1 3 6 9
镜像输出:4
/ \
7 2
/ \ / \
9 6 3 1示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
LeetCode链接:力扣
思路:
两种方式
示例代码:
//递归
TreeNode* mirrorTree(TreeNode* root) {
if (root == nullptr)
{
return root;
}
TreeNode *tempNode = root->left;
root->left = root->right;
root->right = tempNode;
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
//迭代
TreeNode* mirrorTree(TreeNode* root) {
stack sck;
sck.push(root);
while(!sck.empty())
{
TreeNode* tmp = sck.top();
sck.pop();
if(!tmp) continue;
TreeNode *tempNode = tmp->left;
tmp->left = tmp->right;
tmp->right = tempNode;
if(tmp->right != nullptr) sck.push(tmp->right);
if(tmp->left != nullptr) sck.push(tmp->left);
}
return root;
}
题目:
不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印。例如输入{8,6,10,#,#,2,1},如以下图中的示例二叉树,则依次打印8,6,10,2,1(空节点不打印,跳过),请你将打印的结果存放到一个数组里面,返回。
数据范围:
0<=节点总数<=1000
-1000<=节点值<=1000
示例1
输入:{8,6,10,#,#,2,1}
返回值:[8,6,10,2,1]
示例2
输入:{5,4,#,3,#,2,#,1}
返回值:[5,4,3,2,1]
LeetCode链接:力扣
思路:
示例代码:
vector levelOrder(TreeNode* root) {
if (root == nullptr)
{
return vector();
}
vector ret;
queue q; // 队列模拟层次遍历
q.push(root); // 根节点首先入队
while (!q.empty()) {
/*
* 每次外层while循环开始
* 队列中存储的节点都位于二叉树的同一层
* nCount记录此时队列的长度,也就是二叉树某一层的宽度
*/
int nCount = q.size();
while (nCount--) {
/*
* 内层while循环将队列中处在二叉树同一层的所有节点出队
* 并将下一层的所有节点从左到右入队
*/
TreeNode* node = q.front();
ret.push_back(node->val);
q.pop();
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return ret;
}
题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:输入: [1,6,3,2,5]
输出: false
示例 2:输入: [1,3,2,6,5]
输出: true
LeetCode链接:力扣
思路:
示例代码:
bool dfs(vector& postorder, int start, int end)
{
// 单个结点,必然是二叉树
if (start >= end)
{
return true;
}
// 找左子树的结束位置
int nIndex = start;
int rootVal = postorder[end];
while (postorder[nIndex] < rootVal)
{
++nIndex;
}
// 设置左子树结束位置
int leftEnd = nIndex - 1;
// 找右子树的结束位置
while (postorder[nIndex] > rootVal)
{
++nIndex;
}
// 右子树结束位置应该正好在end这里
return nIndex == end && dfs(postorder, start, leftEnd) && dfs(postorder, leftEnd+1, end-1);
}
bool verifyPostorder(vector& postorder) {
return dfs(postorder, 0, postorder.size()-1);
}
题目:
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
示例 2:输入:root = [1,2,3], targetSum = 5
输出:[]
示例 3:输入:root = [1,2], targetSum = 0
输出:[]
LeetCode链接:力扣
思路:
示例代码:
// 维护当前的路径
vector path;
// 结果
vector> res;
vector> pathSum(TreeNode* root, int sum) {
dfs(root, sum);
return res;
}
// 递归求解
void dfs(TreeNode* curr, int target)
{
// 忽略空结点
if (curr != nullptr)
{
target -= curr->val;
path.push_back(curr->val);
if (target != 0 || curr->left != nullptr || curr->right != nullptr)
{
dfs(curr->left, target);
dfs(curr->right, target);
}
else
{
// 满足结果且为根节点
res.push_back(path);
}
// 回溯
path.pop_back();
}
}
题目:
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
LeetCode链接:力扣
思路:
pre
和head
,pre
指针用于保存中序遍历的前一个节点,head
指针用于记录排序链表的头节点root
pre
不为空时,将前驱节点pre
的右指针指向当前根节点root
,即pre->right = root
pre
为空时: 代表正在访问链表头节点,记为 head = root
,保存头节点pre
右移到当前root
节点,接下来递归到右子树重复上述操作示例代码:
Node* pre = nullptr, *head = nullptr;
Node* treeToDoublyList(Node* root) {
if (root == nullptr)
{
return root;
}
dfs(root);
head->left = pre;
pre->right = head;
return head;
}
void dfs(Node* root){
if (root == nullptr)
{
return root;// 递归边界: 叶子结点返回
}
dfs(root->left); //左子树
if (pre)
{
pre->right = root;
}
else
{
head = root; // 保存链表头结点
}
root->left = pre;
pre = root;
dfs(root->right); //右子树
}
题目:
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
LeetCode链接:力扣
思路:
两种方法
示例代码:
//递归
int maxDepth(TreeNode* root) {
if (root == nullptr)
{
return 0;
}
int nLeft = maxDepth(root->left);
int nRight = maxDepth(root->right);
return nLeft > nRight ? nLeft + 1 : nRight + 1;
}
//BFS
int maxDepth(TreeNode* root) {
if (root == nullptr)
{
return 0;
}
queue que;
que.push(root);
while(que.size())
{
for(int i = que.size();i >= 0;--i)
{
auto tmp = que.front();
que.pop();
if(tmp->left) que.push(tmp->left);
if(tmp->right) que.push(tmp->right);
}
count++;
}
return count;
}
题目:
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
LeetCode链接:力扣
思路:
示例代码:
bool isBalanced(TreeNode* root) {
return dfs(root) != -1;
}
int dfs(TreeNode* node)
{
if (node == nullptr)
{
return 0;
}
int left = dfs(node->left);
if (left == -1)
{
return -1;
}
int right = dfs(node->right);
if (right == -1)
{
return -1;
}
return abs(left-right) > 1 ? -1 : max(left, right) + 1;
}
题目:
给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针
思路:
示例代码:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
if (pNode == nullptr)
return nullptr;
TreeLinkNode* node = nullptr;
if (pNode->right != nullptr) {//如果当前节点有右子树,则右子树最左边的那个节点就是
node = pNode->right;
while (node->left != nullptr)
node = node->left;
return node;
}
node = pNode;
while (node->next != nullptr && node == node->next->right) {//找到当前节点是其父亲节点的左孩子的那个节点,然后返回其父亲节点,如果当前节点没有右子树
node = node->next;
}
return node->next;
}
题目:
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:1
/ \
2 2
\ \
3 3示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:输入:root = [1,2,2,null,3,null,3]
输出:false
LeetCode链接:力扣
思路:
示例代码:
bool isSymmetric(TreeNode* root) {
if (root == nullptr)
{
return true;
}
return help(root->left,root->right);
}
bool help(TreeNode* leftNode,TreeNode* rightNode)
{
if (leftNode == nullptr && rightNode == nullptr)
{
return true;
}
if (leftNode == nullptr || rightNode == nullptr)
{
return false;
}
return leftNode->val == rightNode->val && help(leftNode->left,rightNode->right) && help(leftNode->right, rightNode->left);
}
题目:
给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
数据范围:0 ≤ n ≤15000,树上每个节点的val满足 |val| <= 100
例如:
给定的二叉树是{1,2,3,#,#,4,5}该二叉树之字形层序遍历的结果是
[
[1],
[3,2],
[4,5]
]
示例1
输入:{1,2,3,#,#,4,5}
返回值:[[1],[3,2],[4,5]]
示例2
输入:{8,6,10,5,7,9,11}
返回值:[[8],[10,6],[5,7,9,11]]
示例3
输入:{1,2,3,4,5}
返回值:[[1],[3,2],[4,5]]
思路:
两种方式:
示例代码:
//使用队列
vector > Print(TreeNode* pRoot) {
vector > ret;
if (pRoot == nullptr)
{
return ret;
}
queue q; // 定义队列
q.push(pRoot); // 根结点入队列
int level = 0;
while (!q.empty()) {
vector arr; // 定义数组存储每一行结果
int size = q.size(); // 当前队列长度
for (int i = 0; i < size; i++) {
TreeNode* tmp = q.front(); // 队头元素
q.pop();
if (tmp == nullptr) // 空元素跳过
{
continue;
}
q.push(tmp->left); // 左孩子入队列
q.push(tmp->right); // 右孩子入队列
if (level % 2 == 0)
{
// 从左至右打印
arr.push_back(tmp->val);
} else
{ // 从右至左打印
arr.insert(arr.begin(), tmp->val);
}
}
level++; // 下一层,改变打印方向
if (!arr.empty())
{
ret.push_back(arr); // 放入最终结果
}
}
return ret;
}
//使用栈
vector > Print(TreeNode* pRoot) {
vector > ret;
if (pRoot == nullptr)
{
return res;
}
stack stk1, stk2; // 定义两个栈
stk1.push(pRoot); // 根结点入栈
while (!stk1.empty() || !stk2.empty()) { // 两个栈全空时循环结束
vector arr;
while (!stk1.empty()) {
TreeNode* p = stk1.top();
stk1.pop();
arr.push_back(p->val); // 访问栈顶
if (p->left)
{
stk2.push(p->left); // 左孩子入栈
}
if (p->right)
{
stk2.push(p->right); // 右孩子入栈
}
}
if (arr.size())
{
res.push_back(arr); // 保存结果
}
arr.clear(); // 清空
while (!stk2.empty()) {
TreeNode* p = stk2.top();
stk2.pop();
arr.push_back(p->val); // 访问栈顶
if (p->right)
{
stk1.push(p->right); // 右孩子入栈
}
if (p->left)
{
stk1.push(p->left); // 左孩子入栈
}
}
if (arr.size())
{
ret.push_back(arr); // 保存结果
}
}
return ret;
}
题目:
请实现两个函数,分别用来序列化和反序列化二叉树。
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
示例:
输入:root = [1,2,3,null,null,4,5]
输出:[1,2,3,null,null,4,5]
LeetCode链接:力扣
思路:
示例代码:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string str;
help1(root,str);
return str;
}
//先序遍历
void help1(TreeNode* root,string &str)
{
if (root == nullptr)
{
str += "#,";
}
else
{
str += to_string(root->val) + ",";
help1(root->left,str);
help1(root->right,str);
}
}
// Decodes your encoded data to tree.
//解析特殊字符,存入vector
TreeNode* deserialize(string data) {
vector vec;
string str;
for (auto &ch : data)
{
if (ch == ',')
{
vec.push_back(str);
str = "";
}
else
{
str.push_back(ch);
}
}
if (!str.empty())
{
vec.push_back(str);
str = "";
}
return help2(vec);
}
//递归建树
TreeNode *help2(vector &vec)
{
if (vec.front() == "#")
{
vec.erase(vec.begin());
return nullptr;
}
TreeNode *root = new TreeNode(stoi(vec.front()));
vec.erase(vec.begin());
root->left = help2(vec);
root->right = help2(vec);
return root;
}
题目:
给定一个节点数为 n 二叉树,要求从上到下按层打印二叉树的 val 值,同一层结点从左至右输出,每一层输出一行,将输出的结果存放到一个二维数组中返回。
例如:
给定的二叉树是{1,2,3,#,#,4,5}该二叉树多行打印层序遍历的结果是
[
[1],
[2,3],
[4,5]
]
思路:
两种方式:
示例代码:
//递归
vector > Print(TreeNode* pRoot) {
vector > ret;
if (pRoot == nullptr)
{
return ret;
}
helper(ret, 0, pRoot);
return ret;
}
void helper(vector > & ret, int level, TreeNode* node) {
if (node == nullptr) // 结点为空,直接返回
{
return;
}
if (ret.size() == level)
{ // 遍历到新的一层时
vector tmp;
ret.push_back(tmp);
}
ret[level].push_back(node->val); // 保存结果(第level层)
helper(ret, level + 1, node->left); // 左孩子
helper(ret, level + 1, node->right); // 右孩子
}
//非递归
vector > Print(TreeNode* pRoot) {
vector > ret;
if (pRoot == nullptr)
{
return ret;
}
queue q; // 定义队列
q.push(pRoot); // 根结点入队
while (!q.empty()) {
int size = q.size();
vector arr;
for (int i = 0; i < size; i++) {
TreeNode* p = q.front();
q.pop();
if (p->left) q.push(p->left); // 左孩子入队
if (p->right) q.push(p->right); // 右孩子入队
if (!p)
{
continue;
}
arr.push_back(p->val);
}
ret.push_back(arr);
arr.clear();
}
return ret;
}
题目:
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
示例 1:
输入:root = [3,1,4,null,2], k = 1
输出:1
示例 2:输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
LeetCode链接:力扣
思路:
示例代码:
vector num;
int kthSmallest(TreeNode* root, int k) {
dfs(root);
return num[k - 1];
}
void dfs(TreeNode *node) {
if (node->left != NULL)
{
dfs(node->left);
}
num.push_back(node->val);
if (node->right != NULL)
{
dfs(node->right);
}
}
题目:
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:1
示例 2:输入:n = 5
输出:5
LeetCode链接:力扣
思路:
两种方法:
示例代码:
//递归
int fib(int n) {
if (n <= 1)
{
return n;
}
return (fib(n-1) + fib(n-2))%1000000007;
}
//迭代
int fib(int n) {
if(n <= 0)
{
return 0;
}
int first = 0;
int second = 1;
for (int i = 2; i <=n;i++)
{
int temp = first + second;
first = second;
second = temp%1000000007;
std::cout <
题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:输入:n = 7
输出:21
示例 3:输入:n = 0
输出:1
LeetCode链接:力扣
思路:
示例代码:
int numWays(int n) {
if (n <=1)
{
return 1;
}
int first = 1;
int second = 2;
for (int i = 3; i <= n;i++)
{
int nTemp = first + second;
first = second;
second = nTemp%1000000007;
}
return second;
}
题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
思路:
示例代码:
//递归
int jumpFloorII(int number) {
if(number==1) return 1;
return 2*jumpFloorII(number-1);
}
//迭代
int jumpFloorII(int number) {
if(number==1) return 1;
int count=0,a=1;
for(int i = 2;i<=number;++i){
count=a*2;
a=count;
}
return count;
}
题目:
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。
请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
比如n=3时,2*3的矩形块有3种覆盖方法:
示例代码:
//递归:
int rectCover(int number) {
if(number<=2) return number;
return rectCover(number-1)+rectCover(number-2);
}
//迭代
int rectCover(int number) {
if (number <= 2) {
return number;
}
int first = 1, second = 2, third = 3;
for (int i = 3; i <= number; ++i) {
third = first + second;
first = second;
second = third;
}
return third;
}
题目:
给你一根长度为 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。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
LeetCode链接:力扣
思路:
示例代码:
int cuttingRope(int n)
{
if(n <= 1) return 0;
if(n == 2) return 1;
if(n == 3) return 2;
vector dp(n + 1,0);
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
for(int i = 4;i <= n;i++)
{
for(int j = 1;j <= i / 2;j++)
{
dp[i] = max(dp[j] * dp[i - j],dp[i]);
}
}
return dp[n];
}
题目:
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。
示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例 2:输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
LeetCode链接:力扣
思路:
DFS 解析:
示例代码:
bool exist(vector>& board, string word) {
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
int rows, cols;
bool dfs(vector>& board, string word, int i, int j, int k) {
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
if(k == word.size() - 1) return true;
board[i][j] = '\0';
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = word[k];
return res;
}
题目:
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 2:输入:m = 3, n = 1, k = 0
输出:1
LeetCode链接:力扣
思路:
示例代码;
int movingCount(int m, int n, int k)
{
vector> vec(m,vector(n,false));
return dfs(0,0,m,n,k,vec);
}
int dfs(int x, int y,int m,int n,int k,vector> &vec)
{
if (x < 0 || y< 0 || x >= m || y >= n || vec[x][y] || get(x,y) > k)
{
return 0;
}
vec[x][y] = true;
return dfs(x+1,y,m,n,k,vec) + dfs(x-1,y,m,n,k,vec) + dfs(x,y+1,m,n,k,vec) + dfs(x,y-1,m,n,k,vec) + 1;
}
int get(int x, int y)
{
int ret = 0;
while(x)
{
ret += x %10;
x = x/10;
}
while(y)
{
ret += y%10;
y = y/10;
}
return ret;
}
题目:
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数。
示例1
输入
2.00000,3
Copy to clipboardErrorCopied
返回值
8.00000
Copy to clipboardErrorCopied
示例2
输入
2.10000,3
Copy to clipboardErrorCopied
返回值
9.26100
Copy to clipboardErrorCopied
示例3
输入
2.00000,-2
Copy to clipboardErrorCopied
返回值
0.25000
Copy to clipboardErrorCopied
LeetCode链接:力扣
思路:
示例代码:
//直接遍历
double myPow(double x, int n) {
if (x == 0) return 0.0;
if (n == 0) return 1.0;
bool bRet = false;
if (n < 0)
{
n = n*(-1);
bRet = true;
}
double temp = 1;
while(n-- >0)
{
temp*=x;
}
return bRet?1/temp:temp;
}
//快速幂
double myPow(double x, int n) {
if( n == 0) return 1;
if( x == 0.0) return 0;
long exp = n;//
if(n < 0) {
exp = n* (-1.0);
}
double res = 1.0;
while(exp != 0){
if( (exp &1) == 1 ){
res *=x;
}
x *=x;
exp >>= 1;
}
return n<0 ? 1/res: res;
}
题目:
求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3
输出: 6
示例 2:输入: n = 9
输出: 45
LeetCode链接:力扣
思路:
示例代码;
int Sum_Solution(int n) {
int sumNum = n;
bool ans = (n > 0) && ((sumNum += Sum_Solution(n - 1)) > 0);
return sumNum;
}
题目:
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
示例:
输入: a = 1, b = 1
输出: 2
LeetCode链接:力扣
思路:
不考虑进位,对每一位相加,0+0,1+1的结果是0,0+1,1+0的结果都是1(这异或的结果一样)
进位:0+0,0+1,1+0都不会产生进位;只有1+1才会向前生产一个进位(此时我们可以想象成两个数先做位与运算,然后再向左移动一位)
把前两步的结果相加:第三步相加的的过程依然是重复前两步,直到不产生进位为止
示例代码:
int add(int a, int b) {
if (a == 0 || b == 0) {
return a == 0 ? b : a;
}
int sum = 0, carry = 0;
while (b != 0) { // 当没有进位的时候退出循环
sum = a ^ b;
carry = (unsigned int) (a & b) << 1; // C++ 不允许负数进行左移操作,故要加 unsigned int
a = sum;
b = carry;
}
return a;
}
题目:
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。示例 1:
输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:输入:n = 128 (控制台输入 00000000000000000000000010000000)
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
Leecode链接:力扣
思路:
示例代码:
//遍历
int hammingWeight(uint32_t n) {
int nCount = 0;
while(n > 0)
{
if (n % 2 == 1)
{
nCount++;
}
n = n/2;
}
return nCount;
}
//位运算
int hammingWeight(uint32_t n) {
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
题目:
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:输入:arr = [0,1,2,1], k = 1
输出:[0]
LeetCode链接:力扣
思路:
两种方式:
示例代码:
//排序,取k个值
vector getLeastNumbers(vector& arr, int k) {
if (k <=0 || arr.size() <= 0)
{
return vector();
}
if (k >= arr.size())
{
return arr;
}
std::sort(arr.begin(),arr.end())
vector ret;
for (int i = 0 ;i < k ;i++)
{
ret.push_back(arr[i]);
}
return ret;
}
//优先队列
vector getLeastNumbers(vector& arr, int k) {
if (k <=0 || arr.size() <= 0)
{
return vector();
}
if (k >= arr.size())
{
return arr;
}
priority_queue, greater> pq;
for (auto & a : arr)
{
pq.push(a);
}
vector ret;
while(k--)
{
ret.push_back(pq.top());
pq.pop();
}
return ret;
}
题目:
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12
输出:5
示例 2:输入:n = 13
输出:6
LeetCode链接:力扣
思路:
示例代码:
int countDigitOne(int n) {
long digit = 1; // digit 需为 long 型,因为比如 n 是 INT_MAX,digit 会在最后一次循环越界
int high = n / 10, cur = n % 10, low = 0;
int res = 0;
while (high != 0 || cur != 0) {
if (cur == 0) {
res += high * digit;
}
else if (cur == 1) {
res += high * digit + low + 1;
}
else {
res += (high + 1) * digit;
}
low += cur * digit;
cur = high % 10;
high /= 10;
digit *= 10;
}
return res;
}
题目:
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
LeetCode链接:力扣
思路:
示例代码:
int nthUglyNumber(int n) {
int two = 0, three = 0, five = 0;
vector dp(n);
dp[0] = 1; // dp 初始化
for (int i = 1; i < n; ++i) {
int t1 = dp[two] * 2, t2 = dp[three] * 3, t3 = dp[five] * 5;
dp[i] = min(min(t1, t2), t3);
if (dp[i] == t1) {++ two;}
if (dp[i] == t2) {++ three;}
if (dp[i] == t3) {++ five;}
}
return dp[n - 1];
}
题目:
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
LeeCode链接:力扣
思路:
示例代码:
vector> findContinuousSequence(int target) {
int i = 1, j = 2, s = 3;
vector> res;
while(i < j) {
if(s == target) {
vector ans;
for(int k = i; k <= j; k++)
ans.push_back(k);
res.push_back(ans);
}
if(s >= target) {
s -= i;
i++;
} else {
j++;
s += j;
}
}
return res;
}
题目:
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
LeetCode链接:力扣
思路:
示例代码:
vector twoSum(vector& nums, int target) {
vector vecTemp(2,-1);
int nLeft = 0;
int nRight = nums.size()-1;
while(nLeft <= nRight)
{
if (nums[nLeft] + nums[nRight] == target)
{
vecTemp[0] = nums[nLeft];
vecTemp[1] = nums[nRight];
break;
}
else if (nums[nLeft] + nums[nRight] > target)
{
nRight--;
}
else if (nums[nLeft] + nums[nRight] < target)
{
nLeft++;
}
}
return vecTemp;
}
题目:
从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
示例 1:
输入: [1,2,3,4,5]
输出: True示例 2:
输入: [0,0,1,2,5]
输出: True
LeetCode链接:力扣
思路:
示例代码:
bool isStraight(vector& nums) {
sort(nums.begin(),nums.end());
int nCount = 0;
int nSum = 0;
for (int nIndex = 0;nIndex < nums.size()-1;nIndex++)
{
if (nums[nIndex] == 0)
{
nCount++;
continue;
}
if (nums[nIndex] == nums[nIndex+1])
{
return false;
}
nSum += nums[nIndex+1] - nums[nIndex] -1;
}
return nSum <= nCount;
}
题目:
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出: 3
示例 2:输入: n = 10, m = 17
输出: 2
LeetCode链接:力扣
思路:
示例代码:
//约瑟夫问题
int lastRemaining(int n, int m) {
int pos = 0; // 最终活下来那个人的初始位置
for(int i = 2; i <= n; i++){
pos = (pos + m) % i; // 每次循环右移
}
return pos;
}
题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]
LeetCode链接:力扣
思路:
每插入一个数之前,先判断两个堆的 size() 是否相等。
若相等,先将这个数插入大顶堆,然后将大顶堆的 top() 插入小顶堆。这么做可以保证小顶堆的所有数永远大于等于大顶堆的 top()。
若不相等,先将这个数插入小顶堆,然后将小顶堆的 top() 插入大顶堆。这么做可以保证大顶堆的所有数永远小于等于小顶堆的 top()。
整个过程我们都动态地做到了平衡两个堆的 size(),即保证它们的 size() 最大只相差了 1
示例代码:
priority_queue, less > maxheap;
priority_queue, greater > minheap;
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;
}
题目:
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
LeetCode链接:力扣
思路:
两种实现方法:
(1)优先队列
(2)单调队列
示例代码:
//优先队列
vector maxSlidingWindow(vector& nums, int k) {
int n = nums.size();
priority_queue> q;
for (int i = 0; i < k; ++i) {
q.emplace(nums[i], i);
}
vector ans = {q.top().first};
for (int i = k; i < n; ++i) {
q.emplace(nums[i], i);
while (q.top().second <= i - k) {
q.pop();
}
ans.push_back(q.top().first);
}
return ans;
}
//单调队列
vector maxSlidingWindow(vector& nums, int k) {
if (nums.size() <= 0 || k <= 0 || nums.size() < k)
{
return {};
}
vector ret;
deque q;
for (int i = 0 ;i < nums.size(); i++)
{
while(!q.empty() && nums[i] > nums[q.back()])
{
q.pop_back();
}
while(!q.empty() && i - q.front() >= k)
{
q.pop_front();
}
q.push_back(i);
if (i + 1 >= k)
{
ret.push_back(nums[q.front()]);
}
}
return ret;
}