前言
这里就专门放一些编程题。
正文
技巧
- leetcode 上判定指针是否为空,统一用:
if(head!=NULL)
而不用if(!head)
这个!head
本身就是错的啊,那就意味着这个head要等于NULL,才能进去,这不是有毛病吗。 再强调一遍. - 计算链表的长度使用这样的方式:
code
int len2 = 1;
while(node1->next!=NULL)//这里错第二次了,老是写成node!=NULL,要协程Node->next
{
++len1;
node1 = node1->next;
}
- 这样的错误:
段错误:您的程序发生段错误,可能是数组越界,堆栈溢出(比如,递归调用层数太多)等情况引起.
原因一:由于有一些地方出现了空指针的情况。比如head==nullptr,但我却还使用了head->next;
-
时间超时:
原因一:有可能是你while循环没出来。 -
若遇到要对链表进行切割以及重连的,建议弄一个虚拟节点。
算法题
1. 最长有效括号——动态规划的内容
题目
32. 最长有效括号
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:
输入:s = ""
输出:0
答案
class Solution {
public:
int longestValidParentheses(string s) {
//思路:1. 对字符串的每个字符进行初始化标识
//2. 然后用入栈,与栈元素进行匹配,从而对每个字符重新进行赋值
//3. 然后,最后再用一套for循环判断连续的0有多少个
vector<int> mark(s.length());
stack<int> st;
for(int i = 0;i<s.length();i++)//初始化为0
{
mark[i]=0;
}
for(int i = 0;i<s.length();i++)
{
if(s[i]=='(')
{
st.push(i);
}
else
{
if(st.empty())
{
mark[i]=1;
}
else
{
st.pop();
}
}
}
while(!st.empty())//结束后,栈中还有元素,就意味着这些元素其实是没用的元素
{
mark[st.top()] = 1;
st.pop();
}
int maxLen = 0;
int len = 0;
for(int j = 0;j<s.length();j++)
{
if(mark[j]==1)
{
len = 0;
}
else
{
++len;
maxLen = max(len,maxLen);
}
}
return maxLen;
}
};
题解
先将所有元素都标成0,将不能匹配的右括号全部置为1.遍历一遍,结束后,将不能匹配的左括号也都置为1.最好判断一下连续最长的0是多少个就可以了。
感悟
是不是最长子串这种题目用标识法确实会更好,因为这样更有利于如何去便利哪一部分是最长的。
2. 大数相加
题目
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
示例 1:
输入:num1 = "11", num2 = "123"
输出:"134"
示例 2:
输入:num1 = "456", num2 = "77"
输出:"533"
答案-不需要提前进行反转字符串
string add(string str_1,string_2)
{
string result;
string temp;
bool carryFlag = false;
string maxString=(str_1.size()>=str_2.size())?str_1:str_2;//先得到较长,较短字符串的长度
string minString=(str_1.size()<str_2.size())?str_1:str_2;
//将短字符串补齐到和长字符串一样长,在短字符串前边补上字符0
int maxLength = maxString.size();
int minLength = minString.size();
temp.append((maxLength-minLength),'0').append(minString);
minString.clear();
minString.append(temp);
//字符串进行倒序遍历,按位相机
for(int i=(maxString.size()-1);i>=0;i--)
{
int res;
if(carryFlag)//看一下上一位是否有进位,若有进位要进行加一
res=(maxString[i]-48)+(minString[i]-48)+1;
else
res=(maxString[i]-48)+(minString[i]-48);
if(res<10)//res是否小于10
carryFlag = false;
ese
carryFlag = true;
result.push_back((res%10)+48);//然后将当前的这个值存入result之中
}
if(carryFlag)//若最后一位还有进位,那么就直接在该结果上加一
result.push_back(49);
reverse(result.begin(),result.end());//接下来,翻转整个数组
return result;
}
答案-需要提前进行反转字符串
/*思路:
1. 首先要知道两个知道两个字符串的长度是有可能不一样的。
2. 将两个字符串进行反转。
3. 取一个新的string来存储结果,注意这个string的长度是要比源数据中较长的那个还要多1的,防止有进位。
4. 接下来,从头开始加就可以了,加的终结点放在较短的字符串那里。加的时候,将'0'减掉,然后,进位存储在carry中。值存储在string中。
5. 短的字符串长度加完后,就要开始考虑长的那个字符串了,将长的那个字符串后的部分+之前的进位后,就可以直接跟在result后面了。
6. 最后再提前进行反转就可以了。
*/
public static void main(String[] args) {
String str1 = "123459";
String str2 = "123";
System.out.println(add(str1, str2));//123582
}
private static String add(String str1, String str2) {
//任何一个字符串为null或空字符串,都不需要相加了
if (str1 == null || "".equals(str1)) {
return str2;
}
if (str2 == null || "".equals(str2)) {
return str1;
}
int maxLength = Math.max(str1.length(), str2.length());
//定义一个存贮结果的字符串,长度要比最大长度字符串还长一位,用于存储可能出现的进位
StringBuffer result = new StringBuffer(maxLength + 1);
//翻转两个字符串
str1 = new StringBuffer(str1).reverse().toString();
str2 = new StringBuffer(str2).reverse().toString();
//反转后的结果分别为:
//954321
//321
int minLength = Math.min(str1.length(), str2.length());
//进位
int carry = 0;
//当前位上的数值
int currentNum = 0;
//循环变量
int i = 0;
for (; i < minLength; i++) {
//分别获取两个字符对应的字面数值,然后相加,再加上进位
currentNum = str1.charAt(i) + str2.charAt(i) - 2 * '0' + carry;
//获取进位
carry = currentNum / 10;
//处理当前位的最终值
currentNum %= 10;
//保存当前位的值到最终的字符缓冲区中
result.append(String.valueOf(currentNum));
}
if (str1.length() < str2.length()) {
//选择
str1 = str2;
}
for (; i < str1.length(); i++) {
//分别获取两个字符对应的字面数值,然后相加,再加上进位
currentNum = str1.charAt(i) - '0' + carry;
//获取进位
carry = currentNum / 10;
//处理当前位的最终值
currentNum %= 10;
//保存当前位的值到最终的字符缓冲区中
result.append(String.valueOf(currentNum));
}
//处理最后一个的进位(当循环结束后,是不是还可能会有一个进位)
if (carry > 0) {
result.append(String.valueOf(carry));
}
//最后翻转恢复字符串,再返回
return result.reverse().toString();
}
3. 10亿int型数,统计只出现一次的数
思路:
位图法:用bit来标识一个int整数。
分治法:分批处理这10亿的数值。
4. 层序遍历二叉树
反正就是用队列。
答案
void layerTrace(BTreeNode *T)
{
if(T== nullptr)//先判断这棵树是否为空
return;
BTreeNode *p=T;
queue<BTreeNode*>q;
q.push(p);//然后让头结点先入队列
while(!q.empty())//这里可以去判断队列是否非空
{
p=q.front();//队列中的元素出来,
q.pop();
cout<<<<p->data;
if(p->left!= nullptr)q.push(p->left);//找左子树,让其入队列
if(p->right!= nullptr)q.push(p->right);//找,让其入队列右子树
}
}
5. 两个栈实现一个队列
code
答案
/*思路
1. 入队列就正常入到stak1中就好。
2. 出队的时候,先判断stack2为不为空,不为空的话,直接从stack2中出。为空,则将stack1中的元素全部搬到stack2中。再进行出栈。
*/
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(stack2.size()!=0){
int tmp = stack2.front();
stack2.pop();
return tmp;
}
else{//若栈二为空了,我们就要把栈一的值全都放在栈二中去
while(stack1.size()!=0){
int tmp = stack1.top();
stack1.pop();
stack2.push(tmp);
}
return pop();
}
}
private:
stack<int> stack1;
stack<int> stack2;
};
6. 判断链表中是否有环 ----- 有关单链表中环的问题
其实最重要的就是要明白的一个关系:
就是头节点到入口点的距离 = 相遇点到入口点的距离。
求相遇点就用双指针就可以了。
6.1 判断单链表中是否有环
快慢指针,若两个指针相遇,则证明有环。
答案
//如何判断是否有环
typedef struct node{
char data;
node *next;
};
bool exitLoop(Node *head)
{
Node *fast,*slow;
slow = fast = head;
while(slow!=NULL&&fast->next!=NUL)//这样就可以判断有没有环了,如果没环,第二个节点肯定很早就到达末尾了,不会有slow = fast的这种情况。
{
slow = slow->next;
fast = fast->next->next;
if(slow==fast)
return true;
}
return false;
}
6.2 判断单链表的入口点的地址
相遇点到入口点的距离=头节点到入口点的距离
code
答案
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* head) {
//这题第一眼看到也是没有多少想法,https://www.cnblogs.com/yorkyang/p/10876604.html
ListNode *fast,*slow;
slow = fast = head;
while(slow!=NULL&&fast->next!=NULL)
{
slow = slow->next;
fast = fast->next->next;
if(slow==fast)//这时找到相遇点就停下来
break;
}
if(slow==NULL||fast->next==NULL)//你这里肯定判断有没有环了,若有环才继续下一步
return NULL;
ListNode *ptr1 = head;
ListNode *ptr2 = slow;
while(ptr1!=ptr2)//有一个相等关系:头->入口点 = 两个指针的相遇点->入口点/。这两段距离是相等的
{
ptr1 = ptr1->next;
ptr2 = ptr2->next;
}
return ptr1;
}
};
6.3 如果存在环,求出环上各点的个数。
思路1:记录下相遇节点存入临时变量tempPtr,然后让slow(或者fast,都一样)继续向前走slow = slow -> next;一直到slow == tempPtr; 此时经过的步数就是环上节点的个数;
思路2: 从相遇点开始slow和fast继续按照原来的方式向前走slow = slow -> next; fast = fast -> next -> next;直到二者再次项目,此时经过的步数就是环上节点的个数 。
7. 两个有序的数组,求中位数
题目
4. [寻找两个正序数组的中位数](https://leetcode-cn.com/problems/median-of-two-sorted-arrays/)
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
方法一:
答案
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int size1 = nums1.size();
int size2 = nums2.size();
int totalSize = size1+size2;
int middle1 = INT_MIN;
int middle2 = INT_MAX;
int index1 = 0;
int index2 = 0;
for(int i = 0;i<=totalSize/2;i++)
{
middle1 = middle2;
if(index2>=size2||(index1<size1&&nums1[index1]<nums2[index2]))
middle2 = nums1[index1++];
else
middle2 = nums2[index2++];
}
if((totalSize%2)==0)
return (middle1+middle2)/2.0;
else
return middle2;
}
};
8. 给定一个无序数组,建一个最大堆。
priority_queue<int,vector
答案
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
//采用最小堆的方式
vector<int> arr;
if(input.size()==0||k==0||k>input.size())
return arr;
priority_queue<int,vector<int>> que;//实现优先队列的数据结构,底层实现堆实现 priority_queue<Type,Container,Functional> Type为数据类型,Container为保存数据的容器,Functional为元素比较方式,若不写后两个元素,则容器默认是vector,比较方式默认用operator<,也就是优先队列是大顶堆,队头元素最大。
for(auto x:input)
{
if(que.size()<k)
{
que.push(x);
}
else
{
if(x>que.top())
{
que.pop();
que.push(x);
}
}
}
while(!que.empty())
{
arr.push_back(que.top());
que.pop();
}
return arr;
}
};
10. 调整数组顺序使奇数位于偶数前面
题目
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
答案
/*
1. 新生成一个空的数组,然后,弄两个指针指向头和尾。
2. 弄一个while,将数据从头到尾,从尾到头遍历过去,基本就可以了。
*/
class Solution {
public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * *
* @param array int整型vector * @return int整型vector */
vector<int> reOrderArray(vector<int>& array)
{
if (array.size() == 0)
return array;
vector<int> num(array.size());
int head = 0;
int tail = array.size() - 1;
int index_head = head;
int index_tail = tail;
while (head < array.size() && tail >= 0)
{
if (array[head] % 2 == 1)
{
num[index_head] = array[head];
index_head++;
}
head++;
if (array[tail] % 2 == 0)
{
num[index_tail] = array[tail];
index_tail--;
}
tail--;
}
return num;
}
};
方法二:用一个i来放奇数,不断移动该值。
思路:进行一次遍历,若得到当前数为奇数的话,则将该数到真正存放奇数部分的那一位的后面一位的前部数,都往后移。这样,就相当于把所有的技术都放在前面了。
答案
/*
1. 弄一个奇数的下标,将数组从头开始遍历。
2. 遇到奇数,就将j-1 到i的所有数都向后面移一位。
3. 然后再将temp赋值给array.
*/
class Solution {
public:
void reOrderArray(vector<int>& array)
{
int i = 0;
for (int j = 0; j < array.size(); j++)
{
if (array[j] & 1)//表示是奇数的话
{
int temp = array[j];//先将该奇数进行赋值
for (int k = j - 1; k >= i; --k)//从该奇数的位置,到要存放奇数的i位置
{
array[k + 1] = array[k];//不断的向后移动
}
array[i++] = temp;//然后,将该temp值赋值给放奇数的这个位置
}
}
}
};
11.旋转链表
题目
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
答案
/**
* 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* rotateRight(ListNode* head, int k) {
if(k==0||head==nullptr||head->next==nullptr)
return head;
int n = 1;
ListNode* node = head;
while(node->next!=nullptr)//通过这个可以计算出当前有多少元素
{
node = node->next;
n++;
}
int add = n-k%n;
if(add==n)//若需要左移的元素刚好是n的整数倍
return head;
node->next = head;//收尾相连
while(add--)//根据这个,移到要断开的那个位置
{
node = node->next;
}
ListNode *ret = node->next;
node->next = nullptr;
return ret;
}
};
题解
这题有几个点:
答案
1. 首先,将链表首尾相连
2. 得出链表有多少个元素。
3. 注意移动的元素的数量有可能大于n.所以要取余。
4. 注意,这里的这个add,移动的个数是n-k。
12. 求一个数组最大递增子序列的和
思路:在这个的基础上修改一下就可以了。首先,dp[0]为数组的第一个元素,接下来,dp[i] = max(arr[i-1],dp[i-1]+arr[i-1])哪个更大。然后,还要比较一下之前的最大值,和现在形成的最大值。
答案
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array)
{
int size = array.size();
vector<int> dp(size + 1, 1);
dp[0] = 0;
int ret = array[0];//ret作为返回值
for (int i = 1; i <= size; i++)//由第一个元素到最后一个元素
{
dp[i] = max(array[i - 1], dp[i - 1] + array[i - 1]);//判断加上这个元素更大,还是不加更大
ret = max(ret, dp[i]);
}
return ret;
}
};
12. 最长递增子序列
题目
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
答案
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
if (nums.size() <= 1) return nums.size();
vector<int> dp(nums.size(), 1);
vector<int> count(nums.size(), 1);
int maxCount = 0;
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
count[i] = count[j];
} else if (dp[j] + 1 == dp[i]) {
count[i] += count[j];
}
}
if (dp[i] > maxCount) maxCount = dp[i];
}
}
int result = 0;
for (int i = 0; i < nums.size(); i++) {
if (maxCount == dp[i]) result += count[i];
}
return result;
}
};
13. 完全二叉树怎么判断?
思路:转变思路,如何判断该树不是完全二叉树,就是层序遍历已经输出null节点了,后面却还有节点。
code
答案
class Solution
{
public:
bool isCompleteTree(TreeNode* root)
{
queue<TreeNode*> q;
bool reachNull = false;
q.push(root);
while (!q.empty())
{
TreeNode* cur = q.front();
q.pop();
if (cur == nullptr)
{
reachNull = true;
continue;
}
else
{
if (reachNull)
{
return false;
}
q.push(cur->left);
q.push(cur->right);
}
}
return true;
}
};
15. 链表中将所有的偶数移到奇数后面不改变原来的相对位置?
答案
ListNode* oddEvenList(ListNode* head)
{
if (head == nullptr || head->next == nullptr)
return head;
ListNode* q = NULL;
ListNode* p = head;
while (p)
{
if (p->val & 1)
{
if (q == NULL)
{
swap(head->val, p->val);
q = head;
}
else
{
q = q->next;//相当于q所指的位置,就是所有奇数的最后一位
swap(q->val, p->val);
}
}
p = p->next;
}
return head;
}
16. LCS最大子序列问题
题目
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
答案
class Solution {
public:
int longestCommonSubsequence(string s1, string s2) {
//使用动态规划的方式
int n1 = s1.size();
int n2 = s2.size();
if(n1*n2==0)
return 0;
int dp[n1+1][n2+1];//因为要考虑dp[0]代表的是空串的含义,dp[i][j]代表的是i->j的公共子序列是怎么样的
for(int i = 0;i<=n1;i++)
{
dp[i][0]= 0;
}
for(int j = 0;j<=n2;j++)
{
dp[0][j] = 0;
}
for(int i = 1;i<=n1;i++)
{
for(int j = 1;j<=n2;j++)
{
if(s1[i-1]==s2[j-1])
{
dp[i][j] = dp[i-1][j-1]+1;//相当于就在原来的基础上+1
}
else
{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[n1][n2];
}
};
17. 字母的全排列
code
答案
class Solution {
public:
set<string> tempRes;//为了剔除掉重复的,所以用set
vector<string> Permutation(string str)
{
vector<string> strOut;
if (str.size() == 0)
return strOut;
Recursion(str, 0);
for (auto x : tempRes)
{
strOut.push_back(x);
}
return strOut;
}
/* void swap(string str,int r,int k)
{
char temp = str[k];
str[k] = str[r];
str[r] = temp;
}
*/
void Recursion(string str, int index)
{
if (index == str.size() - 1)//这个只是为了把最后一个也放进去,然后回溯的时候就会把该放的放进去
tempRes.insert(str);
for (int i = index; i < str.size(); i++)
{
swap(str[index], str[i]);
Recursion(str, index + 1);
}
}
};
18. 判断回文链表?
题目
思路
1. 找到整个链表的中间节点的位置。
2. 将中间节点 到 尾节点的这部分进行反转。
3. 反转好后,将头节点到中间节点 与 尾节点(现后半部分的尾节点) 进行一一对比,如果有不同的,则不是回文链表。
code
class Solution {
public:
bool isPalindrome(ListNode* head)
{
if (head == nullptr)
{
return true;
}
// 找到前半部分链表的尾节点,然后反转后半部分链表
ListNode* firstHalfEnd = endOfFirstHalf(head);//先使用快慢指针找到中间节点
ListNode* secondHalfStart = reverseList(firstHalfEnd->next);//将该节点后面的所有节点进行翻转
// 判断是否回文
ListNode* p1 = head;
ListNode* p2 = secondHalfStart;
bool result = true;
while (result && p2 != nullptr)
{
if (p1->val != p2->val)
{
result = false;
}
p1 = p1->next;
p2 = p2->next;
}
// 还原链表并返回结果
firstHalfEnd->next = reverseList(secondHalfStart);
return result;
}
ListNode* reverseList(ListNode* head)
{
ListNode* prev = nullptr;//有一个prev节点指向nullptr
ListNode* curr = head;//cur则指向当前节点
while (curr != nullptr)//当前节点不为空的时候,就要一直进行反转
{
ListNode* nextTemp = curr->next;//先储存下这个next节点
curr->next = prev;//将指针进行反转
prev = curr;//把cur变成pre
curr = nextTemp;//把next变成cur
}
return prev;//容易返回错误,返回成cur了
}
ListNode* endOfFirstHalf(ListNode* head)
{//返回的是链表中间节点的指针
ListNode* fast = head;
ListNode* slow = head;
while (fast->next != nullptr && fast->next->next != nullptr)//由于快指针每一步是有要踏两步的,所以要判断踏两部的结果
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
};
19. 反转链表
答案-迭代法反转链表
/*struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) { }};*/class Solution {
public:
ListNode* ReverseList(ListNode* pHead)
{
ListNode* pReversedHead = NULL;//要有一个新头节点
ListNode* pNode = pHead;//当前节点指向head
ListNode* pPrev = NULL;//pre节点指向空
while (pNode != NULL)
{
ListNode* pNext = pNode->next;//存储下next节点的情况
if (pNext == NULL)//在Next节点已经无了的时候,那么cur节点就是最后一个节点了,那么这个节点就可以是新头了
pReversedHead = pNode;
pNode->next = pPrev;//正常的一个转向操作
pPrev = pNode;//正常的一个赋值操作
pNode = pNext;
}
return pReversedHead;
}
};
答案-递归法反转链表
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* head) {
//遇到这种问题,就直接当做两个节点进行操作就可以了。
if(head==NULL||head->next==NULL)
return head;
//ans表示的是从head->next开始到最后的所有的反转链表,注意,这里返回的是翻转链表的头节点
ListNode* ans = ReverseList(head->next);
head->next->next = head;//第二个节点
head->next = NULL;//然后将当前节点指向NULL.
return ans;
}
};
20、 二叉树的序列化和反序列化
题目
请实现两个函数,分别用来序列化和反序列化二叉树。
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
答案
class Codec {
public:
void rserialize(TreeNode* root, string& str) {
if (root == nullptr) {
str += "None,";
} else {
str += to_string(root->val) + ",";
rserialize(root->left, str);
rserialize(root->right, str);
}
}
string serialize(TreeNode* root) {
string ret;
rserialize(root, ret);
return ret;
}
TreeNode* rdeserialize(list<string>& dataArray) {
if (dataArray.front() == "None") {
dataArray.erase(dataArray.begin());
return nullptr;
}
TreeNode* root = new TreeNode(stoi(dataArray.front()));
dataArray.erase(dataArray.begin());
root->left = rdeserialize(dataArray);
root->right = rdeserialize(dataArray);
return root;
}
TreeNode* deserialize(string data) {
list<string> dataArray;
string str;
for (auto& ch : data) {
if (ch == ',') {
dataArray.push_back(str);
str.clear();
} else {
str.push_back(ch);
}
}
if (!str.empty()) {
dataArray.push_back(str);
str.clear();
}
return rdeserialize(dataArray);
}
};
21. 返回峰值
22. 写生成堆的代码
code
答案
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
void adjustHeap(std::vector<int>& in, int i, int size);
void heapBuild(std::vector<int>& vec);
void heapSort(std::vector<int>& in);
void swap(std::vector<int>& in, int index1, int index2);
void swap(vector<int>& in, int index1, int index2)
{
int tmp = in[index1];
in[index1] = in[index2];
in[index2] = tmp;
}
void adjustHeap(vector<int>& in, int i, int size)
{
int root = i;//当前要调整的节点
int left = i * 2;//调整节点处的左节点
int right = i * 2 + 1;//调整节点处的右节点
int max = root;
if (left<=size && in[left - 1]>in[max-1])//查找较大元素的下标
{
max = left;
}
if (right<=size && in[right - 1]>in[max - 1])
{
max = right;
}
//上面两个if做出的结果是max中的值得索引,是最大值的索引
if (left <= size && max == left)//如果是左子树与当前节点进行交换的话,则调整左节点那边的树
{
swap(in, root - 1, left - 1);//然后进行交换
adjustHeap(in, left, size);//调整这一边的树就可以了,另一边不用调整
}
if (right<=size&&max == right)//如果是右子树与当前节点进行交换的话,则调整右节点那边的树
{
swap(in, root - 1, right - 1);
adjustHeap(in, right, size);
}
}
void heapBuild(vector<int>& vec)
{
for (int i = vec.size() / 2; i > 0; --i)//从1/2处开始到第一个元素,逐渐调整吗
{
adjustHeap(vec, i, vec.size());
}
}
void heapSort(vector<int>& in)
{
heapBuild(in);//第一遍,建堆
for (int size = in.size(); size > 1; --size)
{
swap(in, 0, size-1);
adjustHeap(in, 1, size-1);
}
}
int main() {
vector<int> input = { 1,25,24,34,65,1,214,-89,74 };
heapSort(input);//堆排序
for (auto it : input) {
cout << it << " ";
}
cout << endl;
return 0;
}
23. 二叉树叶节点中的最小深度
答案
解题思路
此题诈一看,以为与最大二叉树是相同的解题方法。实则不然,这道题目,必须要考虑当有左子树为空时,此时求的应该是右子树的节点中子节点的最小深度。这个是核心思想。
代码
方法一:
class Solution {
public:
int getDepth(TreeNode* node) {
if (node == NULL) return 0;
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
// 中
// 当一个左子树为空,右不为空,这时并不是最低点
if (node->left == NULL && node->right != NULL) {
return 1 + rightDepth;
}
// 当一个右子树为空,左不为空,这时并不是最低点
if (node->left != NULL && node->right == NULL) {
return 1 + leftDepth;
}
int result = 1 + min(leftDepth, rightDepth);
return result;
}
int minDepth(TreeNode* root) {
return getDepth(root);//遇到二叉树的,一般就再弄一个函数进行递归
}
};
方法二:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode* root) {
if(root==nullptr)
return 0;
if(root->left==nullptr&&root->right==nullptr)//只有一个节点的话,直接返回
return 1;
int left = 0;
int right = 0;
int minDepthInt =INT_MAX;
if(root->left)
minDepthInt = min(minDepthInt,minDepth(root->left));
if(root->right)
minDepthInt = min(minDepthInt,minDepth(root->right));
return minDepthInt+1;
}
};
24. 求两个数组的交集,返回交集数组
题目
给定两个数组,编写一个函数来计算它们的交集。
答案
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> arr;
if(nums1.size()==0||nums2.size()==0)
return arr;
map<int,int> map;
for(int i = 0;i<nums1.size();i++){//map还是得要一个一个进行初始化的,逃不掉的
map[nums1[i]] = 0;
}
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int i = 0;
int j = 0;
while(i<nums1.size()&&j<nums2.size())
{
if(nums1[i]==nums2[j])
{
if(map[nums1[i]]==0)
{
arr.push_back(nums1[i]);
map[nums1[i]]++;
}
i++;
j++;
}
else if(nums1[i]>nums2[j])
j++;
else if(nums1[i]<nums2[j])
i++;
}
return arr;
}
};
思路:思路还是比较简单的,先把两个数组进行排序,然后用一个map做剔除重复元素的。然后用双指针,一个指向第一个数组的第一个元素,一个指向第二个数组的第一个元素。不断前进
25.给定正整数n,找到若干个完全平方数使得他们的和等于n,求最少的个数
26.判断是否是二叉搜索树
//这二叉搜索树的特性是左子树小于根节点,根节点小于其所有的右子树
答案
class Solution {
public:
bool res = true;
bool IsBalanced_Solution(TreeNode* root) {
dfs(root);
return res;
}
int dfs(TreeNode* root)
{
if(!root) return 0;
int left = dfs(root -> left);
int right = dfs(root -> right);
if(abs(left - right) > 1) res = false;
return max(left, right) + 1;
}
};
27. 求平方根
题目
请你手动编程实现sqrt()函数:输入一个整型数,求该整数的平方根。
注:给出的整型数保证大于0。
输入:4
输出:2
注:样例1
输入:8
输出:2
解释:8 的平方根为 2.82842... 取其整数部分。
注:样例2
答案
一、二分求解法:
第一个思路是使用二分法,猜测一个数,平方后与原数进行比对,确定一个精度范围后即可求解。
#define LIMIT 0.01
int mySqrt(int x)
{
if(x<=0)
return 0;
double hig = double(x);
double low = 0;
double 1st = 0;
double guess = (low+high)/2;
do{
1st = guess;
if(guess*guess>x)
{
hig = guess;
guess = (low+hig)/2;//继续猜数
}
else if(guess*guess<x)
{
low = guess;
guess = (low+hig)/2;
}
else if(guess*guess==x)
break;
}
while(abs(guess-1st)>LIMIT);
return int(guess);
}
二、牛顿迭代法
另一种方法就是使用牛顿迭代法,要求根号n的近似值,先随便猜测一个数x,然后不断令x等于x和a/x的平均数,迭代六七次后,结果就已经相当精确了。
/* 绝对值函数 */
#define ABS(x) \
((x) < 0 ? -(x) : (x))
/* 设置精度 */
#define LIMIT 0.01
int mySqrt(int x) {
/* 处理非正数情况 */
if(x <= 0) { return 0; }
double guess = x;
double last = 0;
do {
last = guess;
guess = (guess + guess/x)/2;
} while(ABS(guess - last) > LIMIT);
return (int)guess;
}
28. 最长递增子序列
答案
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = (int)nums.size();
if(n==0)
return 0;
vector<int> dp(n,0);//申请一个n的空间
for(int i = 0 ;i<n;i++)
{
dp[i] = 1;//每个终点,都先初始化为1
for(int j = 0;j<i;j++)
{
if(nums[j]<nums[i])//有比当前的i点小的元素的话,这个dp[i]就可以进行+1
dp[i] = max(dp[i],dp[j]+1);//每个dp[i]代表的就是在当前这个数之前的最长递增子序列有多少个
}
}
return *max_element(dp.begin(),dp.end());
}
};
29. 两两交换链表中的节点
题目
答案
/**
* 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* swapPairs(ListNode* head) {//需要两两交换相邻的值
if(head==nullptr||head->next==nullptr)
return head;
ListNode* newNode = head->next;
head->next= swapPairs(newNode->next);
newNode->next = head;
return newNode;
}
};
30. 生成堆的代码
31. 二叉树的最小深度
题目
递归的方法一:深度优先遍历
code
答案
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode* root) {
if(root==nullptr)
return 0;
if(root->left==nullptr&&root->right==nullptr)
return 1;
int left = 0;
int right = 0;
int minDepthInt =INT_MAX;
if(root->left)
minDepthInt = min(minDepthInt,minDepth(root->left));
if(root->right)
minDepthInt = min(minDepthInt,minDepth(root->right));
return minDepthInt+1;
}
};
32.滑动窗口的最大值
题目
暴力破解法
code
答案
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> arrOut;
if(nums.size()==0||k==0)
return arrOut;
int r = 1-k;
for(int i = 0;i<=nums.size()-k;i++)
{
int res = nums[i];
for(int j = i;j<i+k;j++)
{
res = max(res,nums[j]);
}
arrOut.push_back(res);
}
return arrOut;
}
};
方法二:动态规划 用笔绘制一下比较容易出来。
code
答案
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
vector<int> res;
int n = num.size();
if(num.size()<size||num.size()<1||size<1)
return res;
int low = 1-size;//因为要保证当high走到size的时候,才会有第一个元素入数组
int high = 0;
deque<int> dp;//双端队列
while(high<n)
{
if(low>=1&&num[low-1]==dp[0])//当目前的最大元素是low-1.已经出了滑窗的话,就要出队列了
{
dp.pop_front();
}
while(!dp.empty()&&num[high]>dp[0])//判断是否大于dp[0]
{
dp.pop_front();
}
while(!dp.empty()&&num[high]>dp[dp.size()-1])//判断是否大于dp的最后一个元素
{
dp.pop_back();
}
dp.push_back(num[high]);//直接加入high元素
if(low>=0)//dp[0]每次都是最大值,所以,直接加入,但注意,要low端滑到0的时候,才可以加入该数组
res.push_back(dp[0]);
low++;
high++;
}
return res;
}
};
33.最小体力合并土堆:https://blog.csdn.net/weixin_44781107/article/details/103085001
34.奇偶链表重排:https://leetcode-cn.com/problems/odd-even-linked-list/comments/
35. 无重复最长子串
code
答案
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size()==0)
return 0;
unordered_set<char> set;
int rk = -1;//右边界
int res = 0;//每次求的最长子串
for(int i = 0;i<s.size();i++)//这个i就是左边界
{
if(i!=0)
{
set.erase(s[i-1]);//只有在i>0的时候才会进行erase
}
while(rk+1<s.size()&&(set.count(s[rk+1])<1))
{
set.insert(s[rk+1]);
rk++;
}
res = max(res,rk-i+1);
}
return res;
}
};
36. 合并两个有序数组
题目
方法一
code
答案
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == nullptr) {
return l2;
} else if (l2 == nullptr) {
return l1;
} else if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
};
方法二:
code
答案
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* preHead = new ListNode(-1);
ListNode* prev = preHead;
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
prev->next = l1;
l1 = l1->next;
} else {
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev->next = l1 == nullptr ? l2 : l1;
return preHead->next;
}
};
37. 用队列实现栈
用队列实现栈
方法一:单个队列解决
code
答案
class MyStack {
public:
queue<int> q;
MyStack() {
}
void push(int x) {
q.push(x);
}
int pop() {
int size = q.size();
size--;
while(size--)
{
q.push(q.front());//把头部的元素移到尾巴,除了最后一个元素
q.pop();
}
int result= q.front();
q.pop();
return result;
}
int top() {
return q.back();
}
bool empty() {
return q.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
方法二:双队列实现栈
code
答案
class MyStack {
public:
queue<int> que1;
queue<int> que2;
MyStack() {
}
void push(int x) {
que2.push(x);//这个就是作为一个辅助队列而已,
while(!que1.empty())//这个是作为一个主队列
{
que2.push(que1.front());
que1.pop();
}
swap(que1,que2);
}
int pop() {
int r = que1.front();
que1.pop();
return r;
}
int top() {
int r = que1.front();
return r;
}
bool empty() {
return que1.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
38.剑指 Offer 03. 数组中重复的数字
题目
答案
方法一:使用set<int>
,时间复杂度O(N),空间复杂度O(N)
code
答案
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
if(nums.size()==0)
{
return 0;
}
set<int> set;
for(int i = 0;i<nums.size();i++)
{
if(set.count(nums[i]))
{
return nums[i];
}
else
{
set.insert(nums[i]);
}
}
return 0;
}
};
方法二:时间复杂度:O(nlogn),空间复杂度O(1)
code
答案
class Solution {
public int findRepeatNumber(int[] nums) {
Arrays.sort(nums);
for(int i = 0;i < nums.length;i++){
if(nums[i] == nums[i+1]){
return nums[i];
}
}
return -1;
}
}
方法三:利用好所有元素都在1-n的这个区间的这个条件,时间复杂度O(n),空间复杂度O(1)