深拷贝!
暴力法 直接AC ,而且效率还不错
哈希表 map用的太妙了
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
Node* pre=nullptr;
Node* cur=head;
if(head==nullptr)
{
return pre;
}
//复制val和next
Node* new_node=new Node(cur->val);
pre=new_node;
cur=cur->next;
Node* res=pre;
while(cur!=nullptr)
{
new_node=new Node(cur->val);
pre->next=new_node;
cur=cur->next;
pre=pre->next;
}
//复制random
cur=head;
pre=res;
while(cur!=nullptr)
{
Node* go=head;
Node* go1=res;
while(go!=nullptr)
{
if(cur->random == go)
{
pre->random=go1;
break;
}
go=go->next;
go1=go1->next;
}
cur=cur->next;
pre=pre->next;
}
return res;
}
};
因为第二次循环 的时候查找很费劲,所以用哈希表很舒服!
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
unordered_map<Node*, Node*> map;
// 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
while(cur != nullptr) {
map[cur] = new Node(cur->val);
cur = cur->next;
}
cur = head;
// 构建新链表的 next 和 random 指向
while(cur != nullptr) {
map[cur]->next = map[cur->next];
map[cur]->random = map[cur->random];
cur = cur->next;
}
// 返回新链表的头节点
return map[head];
}
};
递归!!中序遍历的理解要加深
二叉搜索树的 中序遍历是递增的排序序列!
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* pre,* head;
void inorder(Node* cur)
{
if(cur==nullptr)
return ;
inorder(cur->left);
if(pre!=nullptr)
pre->right=cur;
else
head=cur;
cur->left=pre;
pre=cur;
inorder(cur->right);
}
Node* treeToDoublyList(Node* root) {
if(root==nullptr)
return root;
inorder(root);
head->left=pre;
pre->right=head;
return head;
}
};
全排列问题,DFS+剪枝
- ps:之前做过,但没有很熟练,这次还是不会,有时间再多做几次(现在看代码很简单)
- 需要注意这里 set的妙用,主要是用 find();
- 还要注意这个 swap和for 的搭配
- 具体细节还可以看《LC第一天》前两题
class Solution {
public:
vector<string> permutation(string s) {
dfs(s,0);
return res;
}
private:
vector<string> res;
void dfs(string s,int index)
{
if(index==s.size()-1)
{
res.push_back(s);
return;
}
set<int> st;
for(int i=index;i<s.size();++i)
{
if(st.find(s[i])!=st.end())//重复,因此剪枝
continue;
st.insert(s[i]);
swap(s[i],s[index]); //交换,将s[i]固定在Index的位置
dfs(s,index+1); //递归下一位
swap(s[i],s[index]); //恢复交换
}
}
};
三种解法:
- 哈希表统计
- 数组排序法
摩尔投票法:核心观念正负抵消;关键在于题意 这个数比一半都多。(最佳解法)
class Solution {
public:
int majorityElement(vector<int>& nums) {
int res=nums[0];
int times=1;
for(int i=1;i<nums.size();++i)
{
if(nums[i]==res)
++times;
else
--times;
if(times==0)
{
res=nums[i];
times=1;
}
}
return res;
}
};
经典:topK 问题,很容易出面试题
自己实现 快速排序 O(nlogn) 重点是要会自己手撕快排!
基于快排的数组划分,完成topK就,不排完 O(n)
最大堆 O(nlogk) 红黑树什么的
快速排序,两个核心:哨兵划分+递归!
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
quickSort(arr,0,arr.size()-1);
vector<int> res;
res.assign(arr.begin(),arr.begin()+k);
return res;
}
private:
void quickSort(vector<int>& arr,int left,int right)
{
//子数组长度为1时终止递归
if(left>=right) return;
//哨兵划分操作(以arr[left]作为基准数)
int i=left,j=right;
while(i<j)
{
while(i<j && arr[j]>=arr[left]) j--;
while(i<j && arr[i]<=arr[left]) i++;
swap(arr[i],arr[j]);
}
swap(arr[i],arr[left]);
//递归左(右)子数组执行哨兵划分
quickSort(arr,left,i-1);
quickSort(arr,i+1,right);
}
};
基于快排的数组划分
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if(k>=arr.size()) return arr;
return quickSort(arr,k,0,arr.size()-1)
}
private:
vector<int> quickSort(vector<int>&arr,int k,int left,int right)
{
int i=left,j=right;
while(i<j)
{
while(i<j && arr[j]>=arr[left]) --j;
while(i<j && arr[i]<=arr[left]) ++i;
swap(arr[i],arr[j]);
}
swap(arr[i],arr[left]);
if(i>k) return quickSort(arr,k,left,i-1);
if(i<k) return quickSort(arr,k,i+1,right);
vector<int> res;
res.assign(arr.begin(),arr,begin()+k);
return res;
}
};
C++中 优先队列 为大根堆!可以拿来直接用
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> vec(k,0);
if(k==0) return vec;
priority_queue<int> Q;
for(int i=0;i<k;++i)
{
Q.push(arr[i]);
}
for(int i=k;i<arr.size();++i)
{
if(Q.top()>arr[i])
{
Q.pop();
Q.push(arr[i]);
}
}
for(int i=0;i<k;++i)
{
vec[i]=Q.top();
Q.pop();
}
return vec;
}
};
上述2、3方案对比:
2方案需要修改原数组,如果原数组不能修改了话,还需要拷贝一份数组,空间复杂度就上去了!
3方案,如果把数据看成输入流的话,使用堆的方法是来一个处理一个,不需要保存数据,只需要保存 k 个元素的最大堆。而快速选择的方法需要先保存下来所有的数据,再运行算法。当数据量非常大的时候,甚至内存都放不下的时候,就麻烦了。所以当数据量大的时候还是用基于堆的方法比较好。
动态规划!
代码 不是严格 按照上面的思路,但本质上是一样的!
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res=nums[0];
int cur=nums[0];
for(int i=1;i<nums.size();++i){
cur=cur+nums[i];
if(cur<nums[i])
cur=nums[i];
res=max(res,cur);
}
return res;
}
};
迭代+求整/求余
- 关键在于题意的理解,以及余数什么的乱七八糟的 很容易弄混
- 代码也不是很懂,稀里糊涂的,有问题多研究下面的网址
- https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof/solution/mian-shi-ti-44-shu-zi-xu-lie-zhong-mou-yi-wei-de-6/
class Solution {
public:
int findNthDigit(int n) {
int digit=1;
long long start=1;
long long count=9;
//求出是几位数的范围
while(n>count)
{
n-=count;
digit+=1;
start*=10;
count=digit*start*9;
}
//求出是哪个数
long long num=start+(n-1)/digit;
//确定所求数位在num的哪一数位
vector<int> s;
while(num)
{
s.push_back(num%10);
num=num/10;
}
int len=s.size();
int res=s[len-(n-1)%digit-1];
return res;
}
};