面试高频题

补充的手写代码

C++实现构造函数和析构函数

class Test{
public:
	Test(int a,int b):x(a),y(b){}
	Test(const Test& ob){
		x = ob.x;
		y = ob.y;
	}
	Test& operator=(const Test& ob1){
		x = ob1.x;
		y = ob1.y;
		return this;
	}
	
	~Test();
private:
	int x,y;
};

用C++实现一个string(有时间再看)

//C++实现string 
#pragma once
#include <cstdlib>
#include <iostream>
#include<bits/stdc++.h> 
using namespace std;
class MyString
{
public:
    MyString(const char* pData = nullptr);//普通的构造函数 
    MyString(const MyString& otherStr);//拷贝构造函数 
    ~MyString();

    MyString& operator=(const MyString& otherStr);//赋值构造 
    MyString& operator=(const char* pData);

    char& operator[](unsigned int index);

    MyString operator+(const MyString &str);
    MyString operator+(const char* pData);

    bool operator==(const MyString &str);
    bool operator==(const char* pData);

    bool operator!=(const MyString &str);
    bool operator!=(const char* pData);

    friend ostream& operator<<(ostream& cout, const MyString& str);
    friend istream& operator>>(istream& cin, MyString& str);


private:
    char* m_pData;
    int m_size;
    int m_capacity;
};
ostream& operator<<(ostream& cout, const MyString& str);

istream & operator >> (istream & cin, MyString & str);



/* 普通构造函数 */
MyString::MyString(const char * pData)
{
    if (nullptr == pData)
    {
        m_size = 0;
        m_capacity = 100;
        m_pData = new char[m_capacity]{ 0 };
        return;
    }
    m_size = strlen(pData);
    m_capacity = 2 * m_size + 1;    //开辟2倍大空间,方便后续存储
    m_pData = new char[m_capacity]{ 0 };
    strcpy(m_pData, pData); 
}
/* 拷贝构造函数 */
MyString::MyString(const MyString & otherStr)   
{
    if (nullptr == &otherStr)   //参数传递引用时一定要做非空验证
    {
        m_size = 0;
        m_capacity = 100;
        m_pData = new char[m_capacity] { 0 };
        return;
    }
    m_size = otherStr.m_size;
    m_capacity = otherStr.m_capacity;
    m_pData = new char[m_capacity]{ 0 };
    strcpy(m_pData, otherStr.m_pData);
}

/* 析构函数 */
MyString::~MyString()
{
    if(nullptr != m_pData)
        delete[] m_pData;
}

MyString & MyString::operator=(const MyString & otherStr)
{
    if (this != &otherStr)          //剑指offer写法
    {
        MyString tmpStr(otherStr);  //调用拷贝构造,交换字符串指针
        char* p = tmpStr.m_pData;
        tmpStr.m_pData = m_pData;
        m_pData = p;
    }
    return *this;
}

MyString & MyString::operator=(const char * pData)
{
    MyString tmpStr(pData); //调用构造函数
    char* p = tmpStr.m_pData;
    tmpStr.m_pData = m_pData;
    m_pData = p;
    return *this;
}

/* 返回新对象 */
MyString MyString::operator+(const MyString & str)
{
    if (nullptr == &str)
    {
        return MyString(*this);
    }
    int newSize = m_size + str.m_size + 1;
    char* temp = new char[newSize] {0};
    strcat(temp, m_pData);
    strcat(temp, str.m_pData);
    MyString res(temp);
    delete[] temp;

    return res;
}
/* 返回新对象 */
MyString MyString::operator+(const char * pData)
{
    if (nullptr == pData)
    {
        return MyString(pData);
    }
    int newSize = m_size + strlen(pData) + 1;
    char* temp = new char[newSize] {0};
    strcat(temp, m_pData);
    strcat(temp, pData);
    MyString res(temp);
    delete[] temp;

    return res;
}

char & MyString::operator[](unsigned int index)
{
    return m_pData[index];
}

bool MyString::operator==(const MyString & str)
{
    bool res = false;
    if (nullptr == &str)
        res = false;
    if (this == &str)
        res = true;
    if (strcmp(m_pData, str.m_pData) == 0)
        res = true;
    return res;
}

bool MyString::operator==(const char * pData)
{
    bool res = false;
    if (nullptr == pData)
        res = false;
    if (strcmp(m_pData, pData) == 0)
        res = true;
    return res;
}

bool MyString::operator!=(const MyString & str)
{
    return !operator==(str);
}

bool MyString::operator!=(const char * pData)
{
    return !operator==(pData);
}

ostream& operator<<(ostream& cout, const MyString& str)
{
    if (nullptr == &str)
        return cout;
    cout << str.m_pData;
    return cout;
}

inline istream & operator >> (istream & cin, MyString & str)
{
    if (nullptr == &str)
        return cin;
    memset(str.m_pData, 0, str.m_capacity); //清空数据
    cin >> str.m_pData;
    return cin;
}

int main()
{
    //测试 构造函数 拷贝构造函数
    MyString name = "Laymond";
    MyString copyName = name;
    cout << name << " " << copyName << endl;
    //测试 [] 运算符
    copyName[0] = 'A';
    cout << copyName << endl;
    //测试 + = 运算符
    copyName = name + " is " + "good !";
    cout << copyName << endl;

    //测试<< >> == != 运算符
    MyString str1, str2;
    cin >> str1 >> str2;
    cout << "str1:"<<str1 << endl;
    cout << "str2:"<<str2 << endl;
    cout << (MyString("hello") == "hello") << endl;
    cout << (MyString("hello") != MyString("hello")) << endl;
    cout << (MyString("hello") == MyString("hello")) << endl;

    //在参数中引用的地方,一定要做非空验证,比如说如下,如果拷贝构造函数 没有做非空验证 程序会崩溃
    MyString *pStr = NULL;
    MyString str(*pStr);

    return 0;
}

单例模式

class Singleton
{
public:
    ~Singleton(){
        cout<<"destructor called!"<<endl;
    }
    //Singleton(const Singleton&)=delete;
    //Singleton& operator=(const Singleton&)=delete;
    static Singleton& get_instance(){
        static Singleton instance;
        return instance;

    }
private:
    Singleton(){
        cout<<"constructor called!"<<endl;
    }
};

int main(int argc, char *argv[])
{
    Singleton& instance_1 = Singleton::get_instance();
    Singleton& instance_2 = Singleton::get_instance();
    return 0;
}

在这里插入图片描述

手写智能指针?????

在这里插入代码片

用C语言实现C++的继承和多态

typedef void (*FUN)();

struct A{
	FUN fun();
	int a;
};

struct B{
	A _a;//继承
	int b;
}

void fA(){
	printf("A:fun()");
}

void fB(){
	printf("B:fun()");
}

int main(){
	A _a;
	B _b;
	a.fun() = fA;
	b._a.fun() = fB;
	A* p2 = &a;
	p2->fun();
	p2 = (A*)&b;
	p2->fun();
	
	return 0;
}

手写二分查找

	int search(vector<int>& nums, int target) {
        // write code here
        int l=0,r=nums.size()-1;
        int mid;
        while(l<=r){
        	mid = l+(r-l)/2;//这个mid是放在这里的
            if(target==nums[mid])
                return mid;
            }
            else if(target>nums[mid])
                r=mid-1;
            else
                l=mid+1;
        }
        return -1;
    }
C语言指针版本
#include <iostream>
using namespace std;
//归并函数
void merge(int *R, int low, int mid,int high)
{
	//需要定义一个额外的数组,临时存放归并的结果
	int maxSize = high - low + 1;
	int temp[7];//这里的大小是原数组的大小 
	for(int k=0;k<=high;k++)
		temp[k]=R[k];
	int i,j,k;
	for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
		if(temp[i]<=temp[j])
			R[k]=temp[i++];
		else
			R[k]=temp[j++];
	}
	while(i<=mid)
		R[k++]=temp[i++];
	while(j<=high)
		R[k++]=temp[j++];	
}

void mergeSort(int *R, int low, int high)
{
	if (low<high){
		int mid = (low + high) / 2;
		mergeSort(R, low, mid);
		mergeSort(R, mid + 1, high);
		merge(R, low, mid,high);
	}
}

int main()
{
	int R[] = {49, 36, 24, 65, 97, 6, 25};
	int n = 7;
	mergeSort(R, 0, n-1);
	for (int i = 0; i < n; ++i)
		cout << R[i] << " ";
	cout<<endl;
	return 0;
}

树的遍历完整代码:

思路:先用前序和中序建树,再遍历

最主要还是二叉树的结构体:
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}

合并K个链表(用堆)(不熟-1)堆要手写

class Solution {
struct cmp{//注意这里是怎么重载的
    bool operator()(ListNode *a, ListNode* b){
        return a->val > b->val;//C++默认大根堆,这里变成小根堆
     }
};
    
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
//         priority_queue<ListNode*,vector<ListNode*>,less<ListNode*>> q;
        priority_queue<ListNode*, vector<ListNode*>,cmp> q;
        for(int i=0;i<lists.size();i++){
            if(lists[i])//注意这里要判断
                q.push(lists[i]);
        }
        ListNode* head = new ListNode(-1);
        ListNode* tem = head;
        while(!q.empty()){
            ListNode* f = q.top();
            q.pop();
            if(f->next)
                q.push(f->next);
            tem->next = f;
            tem = tem->next;
        }
        return head->next;
    }
};
暴力做法:
class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        ListNode* ans = NULL;
        for(int i=0;i<lists.size();i++){
            ans = test(ans,lists[i]);
        }
        return ans;
    }
    
    ListNode* test(ListNode* p1,ListNode* p2){
        if(!p1)
            return p2;
        if(!p2)
            return p1;
        ListNode* p = NULL;
        if(p1->val<p2->val){
            p=p1;
            p->next = test(p1->next,p2);
        }
        else{
            p=p2;
            p->next = test(p1,p2->next);
        }
        return p;
    }
};

14、剪绳子(不熟+1+1)

思路:
在这里插入图片描述

class Solution {
public:
    int cuttingRope(int n) {
        vector<int> dp(n+1,1);//这里
        for(int i=1;i<=(n+1)/2;i++){//这里
            for(int j=i;j<=n;j++){//这里
                dp[j] = max(dp[j],dp[j-i]*i);//这里
            }
        }
        return dp[n];
    }
};

19、回文链表(这个方法不太好)

还有一种方法是用快慢指针,找到中间节点,前半部分和后半部分比较遍历
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        //我想的方法是链表数据存入数组,然后再判断
        if(head==NULL) return true;
        vector<int> help;
        while(head!=NULL)
        {
            help.push_back(head->val);
            head=head->next;
        }
        for(int i=0;i<help.size()/2;i++)
            if(help[i]!=help[help.size()-1-i]) return false;
        return true;
    }
};

22、二叉搜索树第k小节点

中序遍历,存数组
class Solution {
public:
    TreeNode* KthNode(TreeNode* p,unsigned int k)
    {
        if(p==NULL||k<=0)
            return NULL;
        vector<TreeNode*> res;
        test(p,res);
        if(k>res.size())
            return NULL;
        return res[k-1];
    }
     
    void test(TreeNode* &p,vector<TreeNode*> &res){
        if(p==NULL)
            return;
        test(p->left,res);
        res.push_back(p);
        test(p->right,res);
    }
 
     
};

25、链表中倒数第k个节点以及删除操作(不熟+1)

快慢指针
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* p1 = head;
        ListNode* p2 = head;
        while(k--&&p1){
            //应该判断一下k是否大于链表的长
            p1 = p1->next;
        }
        // p1 = p1->next;
        while(p1){
            p1 = p1->next;
            p2 = p2->next;
        }
        return p2;
    }
};
删除倒数第k个结点
ListNode* removeNthFromEnd(ListNode* head, int n) {
        // write code here
        if(head==NULL)
            return NULL;
        ListNode* pre = new ListNode(-1);
        pre->next = head;
        ListNode* first = pre;//注意
        ListNode* slow = pre;
        while(n--&&first){
            first = first->next;
        }
        while(first->next){//注意
            slow = slow->next;
            first = first->next;
        }
        ListNode* tmp = slow->next;
        slow->next = slow->next->next;
        delete tmp;
        return pre->next;
    }

26、数值的整数次方(不熟)

在这里插入图片描述

快速乘法+位运算
class Solution {
public:
    double myPow(double x, int n) {
        long num = n;
        if(n<0){
            num = -num;
            x = 1/x;
        }
        double res = 1;
        while(num){//
            if(num&1)//
                res*=x;//
            x*=x;//
            num>>=1;//
        }
        return res;
    }
};

27、构建乘积数组(不熟)

在这里插入图片描述

先左乘后右乘
class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        vector<int> t(a.size(),1);
        int l=1,r=1;
        for(int i=0;i<t.size();i++){
            t[i]*=l;
            l*=a[i];
        }
        for(int i=t.size()-1;i>=0;i--){
            t[i]*=r;
            r*=a[i];
        }
        return t;
    }
};

28、括号序列(不熟)

bool isValid(string s) {
        // write code here
        stack<char> c;
        for(int i = 0 ; i < s.length(); i++)
        {
            if(c.empty())
            {
                c.push(s[i]);
                continue;
            }
            if(s[i]==')'&&c.top()=='('){c.pop();}//注意if   else
            else if(s[i]=='}'&&c.top()=='{'){c.pop();}
            else if(s[i]==']'&&c.top()=='['){c.pop();}
            else{ c.push(s[i]);}
        }
        return c.empty();
    }

29、大数相加(字符串)(不熟+1+1)

加法的进位永远不可能大于1
class Solution {
public:
    string solve(string s, string t) {
        if(s.empty())return t;
        if(t.empty())return s;
        if(s.size()<t.size())swap(t, s);//保证t的长度小
        int tail=s.size()-t.size();
        int carry=0,tmp=0;
        while(tail--) t='0'+t;
        for (int i=s.size()-1;i>=0;i--){
            tmp=s[i]-'0'+t[i]-'0'+carry;
            if(tmp>=10){
                carry=1;//
                tmp-=10;//
            }
            else{
                carry=0;
            }
            s[i]=tmp+'0';
        }
        if (carry==1){
            s='1'+s;
        }
        return s;
    }
};

链表相加(不熟)

+反转链表
ListNode* addInList(ListNode* head1, ListNode* head2) {
        // write code here
        stack<int> st1;//存储链表1的数据
	    stack<int> st2;//存储链表2的数据
	    while(head1||head2)//head1和head2都遍历完时推出循环//注意
	    {
	        if(head1)
	        {
		        st1.push(head1->val);
		        head1=head1->next;
	        }
	        if(head2)
	        {
		        st2.push(head2->val);
		        head2=head2->next; 
	        }
	    }
	    ListNode *vhead=new ListNode(-1);
	    int carry=0;
	    while(!st1.empty()||!st2.empty()||carry!=0)//这里设置carry!=0,是因为当st1,st2都遍历完时,如果carry=0,就不需要进入循环了
	    {
	        int a=0,b=0;
	        if(!st1.empty())
	        {
	            a=st1.top();
	            st1.pop();
	        }
	        if(!st2.empty())
	        {
	            b=st2.top();
	            st2.pop();
	        }
	        int get_sum=a+b+carry;//每次的和应该是对应位相加再加上进位
	        int ans=get_sum%10;//对累加的结果取余
	        carry=get_sum/10;//如果大于0,就进位
	        ListNode *cur=new ListNode(ans);//创建节点
	        cur->next=vhead->next;//这里心中右图就能做
	        vhead->next=cur;//每次把最新得到的节点更新到vhead->next中
	    }
	    return vhead->next;
    }

30、大数相乘

substr(5)//表示从下标5开始在结尾
substr(5,8)//表示从下标5到8的数
class Solution {
public:
    string multiply(string num1, string num2) {
        int l1 = num1.size();
        int l2 = num2.size();
        string res(l1 + l2, '0');
        for(int i=l1-1; i>=0; i--) {
            for(int j=l2-1; j>=0; j--) {
                int tmp = (res[i+j+1] - '0') + (num1[i] - '0')*(num2[j] - '0'); 
                res[i+j+1] = tmp%10 + '0';
                res[i+j] += tmp/10;
            }
        }
        for(int i = 0; i < l1+l2; i++){
            if(res[i]!='0') return res.substr(i);
        }
        return "0";
    }
};

二叉搜索树的最近公共祖先节点

这是根据二叉搜索树的特性来的
 TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root->val<p->val&&root->val<q->val)
            return lowestCommonAncestor(root->right,p,q);
        if(root->val>p->val&&root->val>q->val)
            return lowestCommonAncestor(root->left,p,q);
        return root;
    }

35、字符串反转(送分)

下面这个是原地交换的操作,或者1、开辟一个新数组2、直接用reverse函数
class Solution {
public:
    string solve(string str) {
        int len = str.length();
        for(int i = 0 ; i < len/2 ;i++)
        {
                swap(str[i],str[len-1-i]);
        }
        return str;
    }
};

36、求平方根(二分不熟)

int sqrt(int x) {
        // write code here
        long long i = 0;
        while(i*i<x)
            i++;
        if(i*i==x)
            return i;
        return i-1;
    }
二分法:
	int sqrt(int x) {
        // write code here
        if(x<=1)
            return x;
        int l=1,r=x,mid=0;
        while(l<=r){
            mid = l+(r-l)/2;
            if(mid==x/mid)//不要写成mid*mid==x,会内存溢出
                return mid;
            else if(mid>x/mid)
                r = mid-1;
            else
                l = mid+1;
        }
        return r;//注意://结束条件end一定<begin,所以返回end 
    }

38、设计getMin功能的栈(不熟)

牛客网这里的第一个数是op,第二个才是值
	stack<int> s,min_s;
    vector<int> getMinStack(vector<vector<int> >& op) {
        // write code here
        vector<int> res;
        for(int i=0;i<op.size();i++){
            if(op[i][0]==1)
                Push(op[i][1]);
            else if(op[i][0]==2)
                Pop();
            else
                res.push_back(get_min());
        }
        return res;
    }
    
    void Push(int x){
        s.push(x);
        if(min_s.empty()||x<min_s.top())//很奇怪,这里还有先后顺序的
            min_s.push(x);
    }
    
    void Pop(){
        if(!s.empty()){
            if(s.top()==min_s.top())
                min_s.pop();
            s.pop();
        }
    }
    
    int  get_min(){
        return min_s.top();
    }

40、字符串排列

class Solution {
public:
    vector<string> end;
    void test(string str,int num)//str不能加&,不能传引用(+1)
    {
        int n=str.size();
        if(num==n-1)
        {
            end.push_back(str);
        }
        for(int i=num;i<n;i++)
        {
            if(str[num]!=str[i] || num==i)
            {
                swap(str[num], str[i]);
                test(str,num+1);
            }
        }
    }
      
    vector<string> Permutation(string str) {
        test(str,0);
        return end;
    }
};

42、把数组排成最小的数

在这里插入图片描述

class Solution {
public:
    string minNumber(vector<int>& nums) {
        vector<string> res;
        string t;
        for(int i=0;i<nums.size();i++){
            res.push_back(to_string(nums[i]));
        }
        sort(res.begin(),res.end(),[](string &s1,string &s2){return s1+s2<s2+s1;});
        for(int i=0;i<res.size();i++){
            t += res[i];
        }
        return t;
    }
};

46、二叉树深度

下面是非递归做法,是层次遍历
int maxDepth(TreeNode* root) {
        if(root == NULL) return 0; //根点为空返回0
        queue<TreeNode*>q; //开一个队列用于存储遍历树的节点
        int res = 0; //用来存最大高度
        q.push(root);
        int n = 0; //用来存储队列的大小
        while(!q.empty()){ //队列不为空就进行如下操作
            n = q.size();
            for(int i = 0;i < n;i ++){ //将这一层节点的左右孩子全部入队列
                TreeNode* node = q.front(); //取出队列的第一个元素
                q.pop();
                if(node->left) q.push(node->left); //左孩子如果不为空就进队列
                if(node->right) q.push(node->right); //右孩子如果不为空就进队列
            }
            res ++; //每遍历一层res就++
        }
        return res;

    }
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root==NULL)
            return 0;
        int l = maxDepth(root->left);
        int r = maxDepth(root->right);
        return max(l,r)+1;
    }
};

47、平衡二叉树(不熟)

求树高+二叉平衡树的判断
class Solution {
public:
    int test(TreeNode* root){
        if(root==NULL)
            return 0;
        int l = test(root->left);
        int r = test(root->right);
        return r>l?r+1:l+1;
    }

    bool isBalanced(TreeNode* root) {
        if(root==NULL)
            return true;
        int l = test(root->left);
        int r = test(root->right);
        return abs(r-l)<=1&&isBalanced(root->left)&&isBalanced(root->right);
    }
};

49、n的阶乘后面有几个0

class Solution {
public:
    int trailingZeroes(int n) {
        int t=0;
        while(n){
            t+=n/5;
            n/=5;
        }
        return t;
    }
};

面试进展

3-22字节面试中的算法题

合并多个有序数组

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

//堆排序做法 
struct Node
{
	int val;
	int col;
	int row;
	Node(int _val, int _row, int _col) 
		:val(_val)
		,row(_row)
		,col(_col)
	{}
};

//向下调整
void Adjust(vector<Node*>& heap, int parent)
{
	size_t child = parent * 2 + 1;
	while (child < heap.size())
	{
		if (child + 1 < heap.size() && heap[child + 1]->val < heap[child]->val)
			child++;
		if (heap[child]->val < heap[parent]->val)
			swap(heap[child], heap[parent]);
		parent = child;
		child = parent * 2 + 1;
	}
}
void Heap_Sort(vector<Node*>& heap)
{
	int parent = heap.size() / 2 - 1;
	for (int i = parent; i >= 0; --i)
		Adjust(heap, parent);
}
vector<int> MergeArrays(vector<vector<int> >& arrs)
{
	vector<int> res;
	int count = 0;
	vector<Node*> heap;//这里也可以使用优先级队列或者set,不需要排序的过程;
	for (size_t i = 0; i < arrs.size(); ++i)
	{
		heap.push_back(new Node(arrs[i][0],i,0));
		count += arrs[i].size(); //用来确定最终的循环次数
	}
	//先将堆中元素排序
	Heap_Sort(heap);
	while (count--)
	{
		//将堆顶元素(最小值)插入到结果数组中
		res.push_back(heap[0]->val);
		//判断堆顶元素是不是其数组的最后一个元素
		if (heap[0]->col == arrs[heap[0]->row].size() - 1)
		{
			heap[0]->val = INT_MAX;//将堆顶元素替换成最大值;
			heap[0]->col = INT_MAX;
			heap[0]->row = INT_MAX;
		}
		else
		{
			heap[0]->val = arrs[heap[0]->row][heap[0]->col + 1];
			heap[0]->row = heap[0]->row;
			heap[0]->col = heap[0]->col + 1;
		}
		Adjust(heap, 0);
	}
	return res;
}

//暴力做法 
vector<int> test(vector<vector<int> >& arrs){
	vector<int> t;
	for(int i=0;i<arrs.size();i++){
		for(int j=0;j<arrs[i].size();j++){//注意呀这里用 arrs[i].size()
			if(arrs[i][j]!=0)
				t.push_back(arrs[i][j]);
		}
	}
	sort(t.begin(),t.end());//sort()时间复杂度是n*log2n 
	return t;
}

//归并排序
vector<int> Merge(vector<int> &a,vector<int> &b){
	int m = 0,n = 0;
	vector<int> res;
	while(m<a.size()&&n<b.size()){
		if(a[m]<b[n]){
			res.push_back(a[m]);
			m++;
		}
		else{
			res.push_back(b[n]);
			n++;
		}			
	}
	while(m<a.size()){
		res.push_back(a[m]);
		m++;
	}
	while(n<b.size()){
		res.push_back(b[n]);
		n++;
	}
	return res;
}

vector<int>  MergeSort(vector<vector<int> > &arrs){
	vector<int> t;
	for(int i=0;i<arrs.size();i++){
		t = Merge(t,arrs[i]);
	}
	return t;
}

int main()
{
	vector<vector<int>> a{{1,2,7,9},{5,6},{3},{4,8}};//看看这里的vector初始化 
	vector<int> res1 = MergeArrays(a);//堆排序做法 
	vector<int> res2 = test(a);//暴力做法 
	vector<int> res3 = MergeSort(a);//归并排序 
	for (auto c : res3)
		cout << c << " ";
	cout << endl;
	system("pause");
	return 0;
}

三数之和

3-26阿里笔试

对于连续的只含有0和1的数组,拿掉一个数,问最大连续1的个数?

在这里插入图片描述

思想:对一维数组进行分段,按照连续1分段,存入二维数组中去,遍历二维数组,在两两行之间的最大和
#include<iostream>
#include<bits/stdc++.h>
using namespace std;

int test(vector<int>& nums){
	vector<vector<int>> res;
	int k=0;
	for(int i=0;i<nums.size();i++){
		vector<int> t; 
		while(nums[i]==1){
			t.push_back(nums[i]);
			i++;
		}	
	   	res.push_back(t);
 	}
	int t = 0;
	for(int i=1;i<res.size();i++){
		int len = res[i-1].size() + res[i].size();  
		t = max(t,len);
	}
	if(res.size()==1)
		return nums.size()-1;
	return t;
}


int main(){
    int n,m,k;
    vector<vector<int>> res;
    cin>>n;
    for(int i=0;i<n;i++){
        vector<int> t;
        cin>>m;
        for(int j=0;j<m;j++){
            cin>>k;
            t.push_back(k);
        }
        res.push_back(t);
    }
    for(int i=0;i<res.size();i++){
        cout<<test(res[i])<<endl;
    }
    return 0;
}

有关的图的最短路径问题(不会)

4-4美团笔试

1、求集合的子序列

没有任何两个字符相同,空串也是子序列
例如:对于aabc,它的子序列有12个

“ ”、“a”、a、b、c、ab、ab、bc、ac、ac、abc、abc
解释:第一个a和第二个a的下标不同,空串也是子序列

2、球形蛋糕

切n刀,问分成几块
输入
2			表示切几刀
1  0		1表示竖着切,0为经度
0  180		0表示横着切,180表示纬度

3、

/**
 * 小美发明了一个函数:f(x),表示将x的所有正整数因子提取出来之后从小到大排列,
再将数字拼接之后得到的数字串。例如:10的所有因子为1,2,5,10,
那么将这些因子从小到大排序之后再拼接得到的数字串为12510,即f(10)=12510。
 *
小美十分讨厌数字k,如果f(x)中含有某个子串对应的数字等于k,
那么她的不高兴度就会增加1。例如:f(8)=1248,那么其所有的子串为:1,2,4,8,12,24,48,124,248,1248,
只要k等于其中任意一个值,那么小美的不高兴度就会增加1。
对于每一个数,其不高兴度至多增加1。
 *
 * 现在,有一个长度为n的正整数序列,定义其不高兴度为序列中所有数的不高兴度的总和。
小美想要知道自己对于这个序列的总不高兴度为多少。
 */

4、

/**
 * @author zhangbingbing
 * @version 1.0
 * @date 2021/4/4 11:16
小美在路上看到一些小学生在玩跳方格,她也想跟着一起玩。
这个方格被划分为n×n的小方格,即有n行n列。
每一个小方格上面都是一个1~k的正整数。小美想依次从1,2,…,k这个顺序来跳。
一开始小美可以站在任意一个小方格。
从一个方格跳到另一个方格的花费为两个方格的曼哈顿距离。
小美想知道是否可以依照该顺序一直跳到k,如果可以,最小的总花费是多少。

两个格子(x1,y1),(x2,y2)的曼哈顿距离为:|x1-x2|+|y1-y2|。
例如(3,4),(1,6)的曼哈顿距离为|3-1|+|4-6|=4。
 */

5、前缀和

4-7快手面试

合并两个有序链表(✅)

4-7华为笔试

添加链接描述

1、分组游戏

在这里插入图片描述

输入n,表示有几个小朋友
接着输入n行,表示这些小朋友想和谁组队
demo1
6
a b
c d
e f
b c
d a
f e
输出:2
dome2
3
a b
b c
c a
输出;1
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <valarray>
#include <stack>
#include <map>
using namespace std;
int cmp(vector<string> a, vector<string> b) {
    return a[0] < b[0];
}
int main()
{
    int n;
    vector<vector<string> > res;
//    map<string, string> ma;
    string s1, s2;
    vector<string> ve;
    cin>>n;
    while(n--) {
        cin >> s1 >> s2;
        if(s1 > s2)
            swap(s1, s2);
        ve.push_back(s1);
        ve.push_back(s2);
    //    ve2.push_back(s1);
        
        res.push_back(ve);
        ve.clear();
    }
    sort(res.begin(), res.end(), cmp);
    
    int cnt = 0;
    vector<string> tmp = res[0];//选定第一个区间
 //   cout << tmp[0] << " " << tmp[1] << endl;
    for(int i = 1; i < res.size(); ++i) {
        if(res[i][0] > tmp[1]) {//判断后面的区间的左边界,和tmp的右边界相比较(注意这里仅限于大于)
            tmp = res[i];
            cnt++;
        }
        else {
            tmp[0] = min(tmp[0], res[i][0]);
            tmp[1] = max(tmp[1], res[i][1]);
        }
   //     cout << tmp[0] << " " << tmp[1] << endl;
    }
    cnt++;
    cout << cnt << endl;
    return 0;
}

2、计算任务的执行时间

在这里插入图片描述

dome1
输入:
134
0->2
输出:
837

解释,任务都是顺序执行的,但是0->2表示依赖关系,先执行完任务2,才能执行任务0

在这里插入图片描述

3、

3、

在这里插入图片描述

一、数组

1、数组中超过一半的数字(×)

1、排序:时间复杂度O(nlog(n))
2、map :时间复杂度O(n),但是需要开辟额外空间O(n)
3、摩尔投票法:时间复杂度O(n)class Solution {
public:
    int majorityElement(vector<int>& nums) {    
//摩尔投票法,投我++,不投--,超过一半以上的人投我,那我稳赢哇
        int count = 0, candidate = 0;
        for(int n : nums)
        {
            if(count == 0)        candidate = n;

            if(n == candidate)    count++;
            else                  count--;
        }
        return candidate;
    }
};

2、无序数组中未出现的最小正整数(×)

在这里插入图片描述

1、排序+查找,光是排序就要O(nlog(n))
2、下面这个方法的时间复杂度:O(n)
下面这个是用的计数排序
	int minNumberdisappered(vector<int>& arr) {
        // write code here
        int n=arr.size();
        for(int i=0;i<n;++i)
        {
            if(arr[i]>=1&&arr[i]<=n&&arr[i]!=arr[arr[i]-1])
                swap(arr[i],arr[arr[i]-1]);
        }
        for(int i=0;i<n;++i)
        {
            if(arr[i]!=i+1)
                return i+1;
        }
        return n+1;
    }

3、圆圈中最后的剩下元素(×)

一个长度为n的数组,每m个删除一个,求最后的那个数
迭代法
时间复杂度:O(N)
空间复杂度: O(1)
class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        if(n<1||m<1)
            return -1;
        int t=0;
        for(int i=2;i<=n;i++)
            t = (t+m)%i;
        return t;
    }
};

4、扑克牌顺序(√)

class Solution {
public:
    bool IsContinuous( vector<int> nums ) {
        if(nums.size()==0)
            return false;
        int t1=0,t2=0;
        sort(nums.begin(),nums.end());
        int i;
        for(i=0;i<nums.size();i++){
            if(nums[i]==0)
                t1++;
            else
                break;
        }
        for(int j=i+1;j<nums.size();j++){
            if(nums[j]==nums[j-1])
                return false;
            else
                t2+=nums[j]-nums[j-1]-1;
        }
        if(t1>=t2)
            return true;
        return false;
    }
};

5、矩阵查找(×)

		int i = 0, j = matrix[0].size() - 1;
        while(i < matrix.size() && j >= 0){//注意这个边界条件
            if (matrix[i][j] == target){
                return true;
            }
            else if (matrix[i][j] < target){
                i++;
            }
            else{
                j--;
            }
        }
        return false;

6、数组中最长连续子序列(×)

在这里插入图片描述

int MLS(vector<int>& arr) {
        // write code here
//         1、排序+计数
        sort(arr.begin(),arr.end());
        int t = 1;
        int maxt = 1;
        for(int i=1;i<arr.size();i++){
            if(arr[i]-1==arr[i-1])
                t++;
            else if(arr[i-1]!=arr[i])
                t = 1;
            maxt = max(maxt,t);
        }
        return maxt;
    }

7、容器盛水问题(接雨水)(×)

在这里插入图片描述

下面这个是双指针
long long maxWater(vector<int>& height) {
        // write code here
        int left = 0, right = height.size() - 1;
        long long ans = 0;//牛客网这里不加long不行
        int left_max = 0, right_max = 0;
        while (left < right) {
            if (height[left] < height[right]) {
                height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
                ++left;
            }
            else {
                height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
                --right;
            }
        }
        return ans;
    }

8、数组中连续子数组的最大和(✅)

在这里插入图片描述

重置数组,大于0的往后加
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int s = nums[0];//注意保存数组第一个的值
        for(int i=1;i<nums.size();i++){
            if(nums[i-1]>0)//记得要写上>0
                nums[i]+=nums[i-1];
            s = max(s,nums[i]);
        }
        return s;
    }
};

9、顺时针打印矩阵(❌)

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
    	if(matrix.empty())//一定要加这个判断为空的条件❌❌❌注意判断空
            return res;
        int row1 = 0,row2 = matrix.size();
        int col1 = 0,col2 = matrix[0].size();
        vector<int> res;
        while(true){
            for(int i=col1;i<col2;i++){
                res.push_back(matrix[row1][i]);
            }
            row1++;
            if(row1>=row2)//注意等于号
                break;
            
            for(int i=row1;i<row2;i++){
                res.push_back(matrix[i][col2-1]);
            }
            col2--;
            if(col1>=col2)
                break;
            
            for(int i=col2-1;i>=col1;i--){
                res.push_back(matrix[row2-1][i]);
            }
            row2--;
            if(row1>=row2)
                break;
            
            for(int i=row2-1;i>=row1;i--){
                res.push_back(matrix[i][col1]);
            }
            col1++;
            if(col1>=col2)
                break;
        }
        return res;
    }
};

螺旋矩阵

vector<int> res;
        if(matrix.empty())//一定要加这个判断为空的条件
            return res;
        int row1 = 0,row2 = matrix.size();
        int col1 = 0,col2 = matrix[0].size();
        
        while(true){
            for(int i=col1;i<col2;i++){
                res.push_back(matrix[row1][i]);
            }
            row1++;
            if(row1>=row2)//注意等于号
                break;
            
            for(int i=row1;i<row2;i++){
                res.push_back(matrix[i][col2-1]);
            }
            col2--;
            if(col1>=col2)
                break;
            
            for(int i=col2-1;i>=col1;i--){
                res.push_back(matrix[row2-1][i]);
            }
            row2--;
            if(row1>=row2)
                break;
            
            for(int i=row2-1;i>=row1;i--){
                res.push_back(matrix[i][col1]);
            }
            col1++;
            if(col1>=col2)
                break;
        }
        return res;

10、股票的最大利润(❌)

int maxProfit(vector<int>& prices) {
        // write code here
        if(prices.size()<2)
            return 0;
        int first = prices[0];
        int maxP = prices[1] - first;
        for(int i=1;i<prices.size();i++){
            if(prices[i]<first)
                first = prices[i];
            if(prices[i]-first>maxP)
                maxP = prices[i]-first;
        }
        return maxP;
    }

❌11、输出最长递增子序列

在这里插入图片描述

❌12、两栈实现队列

✅13、输出最长不重复子串

在这里插入图片描述

✅14、合并两个有序数组(归并)

❌15、反转单词顺序(美团提前批)

在这里插入图片描述

class Solution {
public:
    string reverseWords(string s) {
        if(s.empty())
            return s;
        int l = 0;
        string t = "";
        for(int i=s.length()-1;i>=0;i--){
            if(s[i]!=' ')
                l++;
            else if(s[i]==' '&&l!=0){
                t+=s.substr(i+1,l) + ' ';
                l=0;
                continue;
            }
                
        }
        if(l!=0)
            t+=s.substr(0,l) + ' ';
        if(t.size()>0)
            t.erase(t.size()-1,1);//删除最后面的空格
        return t;
    }
};

二、链表

1、判断链表中没有环

快慢指针
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* first = head;
        ListNode* slow = head;
        while(first&&first->next){//注意
            first = first->next->next;
            slow = slow->next;
            if(first==slow)
                return true;
        }
        return false;
    }
};

2、就地反转链表+递归做法

1、头插法(画图最快)2、借助数组3、递归
class Solution {
public:
    ListNode* ReverseList(ListNode* head) {
        ListNode* pre = NULL;
        ListNode* p = head;
        ListNode* post = head->next;
        while(p){//注意这里的判断条件
            p->next = pre;
            pre = p;
            p = post;
            post = post->next;
        }
        return pre;
    }
};
递归做法
ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) {
            return head;
        }
        ListNode* newHead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newHead;
    }

3、两个链表的第一个公共节点

思路:看
A+B
B+A
最后面几个数必然一致
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* l1 = headA;
        ListNode* l2 = headB;
        while(l1!=l2){
            l1=l1==NULL?headB:l1->next;
            l2=l2==NULL?headA:l2->next;
        }
        return l1;
    }
};

✅4、链表排序

归并排序:分段递归
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head||!head->next)
            return head;
        ListNode* pre;
        ListNode* slow = head;
        ListNode* first = head;
        while(first&&first->next){//这个快慢指针看来都是这个判断条件
            pre = slow;//注意这里
            slow = slow->next;
            first = first->next->next;
        }
        pre->next=NULL;//注意
        return test(sortList(head),sortList(slow));//注意这里的参数
    }
    ListNode* test(ListNode* p1,ListNode* p2){
        if(!p1)//注意
            return p2;
        if(!p2)
            return p1;
        if(p1->val<p2->val){
            p1->next = test(p1->next,p2);
            return p1;//注意
        }
        else{
            p2->next = test(p2->next,p1);
            return p2;
        }
    }
};

✅5、判断链表环的入口节点

添加链接描述

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* p) {
        ListNode* fast = p;
        ListNode* slow = p;
        while(fast&&fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(fast==slow)//注意这里
                break;
        }
        if(!fast||!fast->next)
            return NULL;
        fast = p;
        while(fast!=slow){//注意这里的判断条件
            fast=fast->next;
            slow = slow->next;
        }
        return fast;
    }
};

链表环

❌6、链表中的节点每k个一组翻转

循环+递归+就地排序
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        // write code here
        if(!head||!head->next||k<=1)
            return head;
        ListNode* p = head;
        int len = 0;
        while(p){
            len++;
            p = p->next;
        }
        if(len<k)
            return head;
        p = head;
        ListNode* pre = new ListNode(0);
        pre->next = NULL;
        ListNode* post = head->next;
        for(int i=0;i<k;i++){
            p->next = pre->next;
            pre->next = p;
            p = post;
            if(post)//注意
                post = post->next;
        }
        ListNode* tail = pre->next;
        while(tail->next)//注意(+1)
            tail = tail->next;
        tail->next = reverseKGroup(p,k);//注意
        return pre->next;
    }
};

❌7、合并两个排序链表(非递归不熟-1)

借用额外的两个指针
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // write code here
        if(l1==NULL)
            return l2;
        if(l2==NULL)
            return l1;
        ListNode* head = (l1->val<=l2->val)?l1:l2;
        ListNode* tail = head;
        l1 = (head==l1)?l1->next:l1;
        l2 = (head==l2)?l2->next:l2;
        while(l1&&l2){
            if(l1->val<=l2->val){
                tail->next = l1;
                l1=l1->next;
            }
            else{
                tail->next = l2;
                l2 = l2->next;
            }
            tail = tail->next;
        }
        tail->next = (l1==NULL)?l2:l1;//注意这里
        return head;
    }
不知道为啥我会写成下面这种,不是if嘛,脑子坏了
		while(l1)
            tail->next = l1;
        while(l2)
            tail->next = l2;
下面这个做法是递归的,最后不用
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* p1 = NULL;
        if(l1==NULL)
            return l2;
        if(l2==NULL)
            return l1;
        if(l1->val<l2->val){
            p1=l1;
            p1->next=mergeTwoLists(l1->next,l2);
        }
        else{
            p1=l2;
            p1->next=mergeTwoLists(l1,l2->next);
        }
        return p1;
    }
};

❌8、删除排序链表的重复节点

添加链接描述

class Solution {
public:
    ListNode* deleteDuplication(ListNode* pH) {
        ListNode* preH = new ListNode(-1);
        preH->next = pH;
        ListNode* pre = preH;
        ListNode* cur = pH;
        while(cur){//注意
            if(cur->next&&cur->val==cur->next->val){
                cur=cur->next;//删除第一个重复节点
                while(cur->next&&cur->val==cur->next->val)
                    cur=cur->next;
                cur=cur->next;//删除最后一个重复节点
                pre->next = cur;//注意这一步
            }
            else{
                pre = cur;
                cur = cur->next;
            }
        }
        return preH->next;
    }
};

添加链接描述

三、树

1、对称二叉树(×)

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root==NULL)
            return true;
        return test(root->left,root->right);
    }

    bool test(TreeNode* l1,TreeNode* l2){
        if(l1==NULL&&l2==NULL)
            return true;
        if(l1==NULL||l2==NULL)
            return false;
        if(l1->val!=l2->val)
            return false;
        return test(l1->left,l2->right)&&test(l1->right,l2->left);
    }
};

2、根节点到叶子节点的和为target的路径(√)

class Solution {
public:
//先序遍历
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        vector<vector<int>> res;
        vector<int> t;
        if(root==NULL)
            return res;
        test(root,res,t,0,target);
        return res;
    }

    void test(TreeNode* &root,vector<vector<int>> &res,vector<int> &t,int cout,int Tsum){
        cout+=root->val;
        t.push_back(root->val);
        bool temp = (root->left==NULL&&root->right==NULL);
        if(temp&&cout==Tsum)
            res.push_back(t);
        if(root->left)
            test(root->left,res,t,cout,Tsum);
        if(root->right)
            test(root->right,res,t,cout,Tsum);
        t.pop_back();
    }
};

3、二叉树镜像(×)

我就用递归做拉倒吧
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if(root==NULL)
            return NULL;
        swap(root->left,root->right);
        mirrorTree(root->left);
        mirrorTree(root->right);
        return root;
    }
};

✅4、二叉树的先中后序非递归遍历

先序递归
class Solution {
public:
    vector<int> res;
    vector<int> preorderTraversal(TreeNode* root) {
        if(root){
            res.push_back(root->val);
            preorderTraversal(root->left);           
            preorderTraversal(root->right);
        }
        return res;
    }
};
先序非递归都要借助栈来实现
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root==NULL)
            return res;
        stack<TreeNode*> s;
        TreeNode* p = root;
        while(!s.empty()||p){//注意
            while(p){//注意
                res.push_back(p->val);
                s.push(p);
                p = p->left;
            }
            p = s.top();
            s.pop();
            p = p->right;
        }
        return res;
    }
};
中序
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root==NULL)
            return res;
        stack<TreeNode*> s;
        TreeNode* p = root;
        while(!s.empty()||p){
            while(p){
                s.push(p);
                p = p->left;
            }
            p = s.top();
            res.push_back(p->val);
            s.pop();
            p = p->right;
        }
        return res;
    }
};
后序
class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        if (root == NULL) {
            return res;
        }
        stack<TreeNode *> s;
        TreeNode *prev = NULL;
        while (root != NULL || !s.empty()) {
            while (root != nullptr) {
                s.push(root);
                root = root->left;
            }
            root = s.top();
            s.pop();
            if (root->right == NULL || root->right == prev) {
                res.push_back(root->val);
                prev = root;
                root = NULL;
            } else {
                s.push(root);
                root = root->right;
            }
        }
        return res;
    }
};

5、重建二叉树(×)

class Solution {
public:
    int findA(vector<int> t,int val){
        for(int i=0;i<t.size();i++){
            if(t[i]==val)
                return i;
        }
        return -1;
    }

    void test(TreeNode* &root,vector<int>& preorder, vector<int>& inorder,int lp,int rp,int li,int ri){
        if(lp>rp||li>ri)
            return ;
        root = new TreeNode(preorder[lp]);
        int pos = findA(inorder,preorder[lp]);
        test(root->left,preorder,inorder,lp+1,lp+pos-li,li,pos-1);
        test(root->right,preorder,inorder,lp+pos-li+1,rp,pos+1,ri);
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size()<0)
            return NULL;
        TreeNode* root = NULL;
        test(root,preorder,inorder,0,preorder.size()-1,0,inorder.size()-1);
        return root;
    }
};

✅6、二叉树的层次遍历即变形(√)

class Solution {
public:
    vector<vector<int> > levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(root==NULL)//这个判断条件必须要加
            return res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int n = q.size();
            vector<int> t;
            for(int i=0;i<n;i++){
                TreeNode* p = q.front();
                t.push_back(p->val);
                q.pop();
                if(p->left)
                    q.push(p->left);
                if(p->right)
                    q.push(p->right);
            }
            res.push_back(t);
        }
        return res;
    }
};
之字形打印
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(root==NULL)
            return res;
        queue<TreeNode*> q;
        q.push(root);//注意细节,不要连入栈都忘记写了
        int count = 0;
        while(!empty(q)){
            int n = q.size();
            vector<int> t;
            for(int i=0;i<n;i++){
                TreeNode* l = q.front();
                t.push_back(l->val);
                q.pop();
                if(l->left)
                    q.push(l->left);
                if(l->right)
                    q.push(l->right);

            }
            if(count%2==1)
                reverse(t.begin(),t.end());
            res.push_back(t);
            count++;
        }
        return res;
    }
};

✅7、二叉树的最近公共祖先节点(不熟)

LeetCode:
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p||root==q||!root)
            return root;
        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        TreeNode* right = lowestCommonAncestor(root->right,p,q);
        if(!left)
            return right;
        if(!right)
            return left;
        return root;
    }
};
牛客网屁事多
	int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        // write code here
        return CommonAncestor(root, o1, o2)->val;
    }
    
    TreeNode* CommonAncestor (TreeNode *root, int o1, int o2) {
        if (root == NULL || root->val == o1 || root->val == o2) { // 超过叶子节点,或者root为p、q中的一个直接返回
            return root;
        }
        TreeNode* left = CommonAncestor(root->left,o1,o2); // 返回左侧的p\q节点
        TreeNode* right = CommonAncestor(root->right,o1,o2); // 返回右侧的p\q节点
        if (left == NULL) {  // 都在右侧
            return right;
        }
        if (right == NULL) { // 都在左侧
            return left;
        }
        return root; // 在左右两侧
    }

❌8、树的子结构

class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A==NULL||B==NULL)
            return false;
        bool res = false;
        if(A->val==B->val)
            res = test(A,B);
        if(!res)
            res = isSubStructure(A->left,B);
        if(!res)
            res = isSubStructure(A->right,B);
        return res;
    }

    bool test(TreeNode* l1,TreeNode* l2){
        if(l2==NULL)
            return true;
        if(l1==NULL)
            return false;
        if(l1->val!=l2->val)
            return false;
        return test(l1->left,l2->left)&&test(l1->right,l2->right);
    }
};

四、排序

数组排序

添加链接描述

#include<iostream>
#include<bits/stdc++.h>//priority_queue引用的库 ,也是sort引用的库
#include<vector>
using namespace std;

//快速排序--------------------------------------------- 
int once_quick_sort(vector<int>& t,int low,int high){
	int key = t[low];
	while(low<high){
//		while(low<high&&key<=t[high--]),这样写出错 ,要写成下面那样 
		while(low<high&&key<=t[high])
			high--;
		if(low<high)
			t[low++] = t[high];
		while(low<high&&key>=t[low])
			low++;
		if(low<high)
			t[high--] = t[low];
	}
	t[low] = key;
	return low;
}

void quick_sort(vector<int>& t,int low,int high){
	if(low>=high)
		return ;
	int mid = 0; 
	mid = once_quick_sort(t,low,high);
	quick_sort(t,low,mid-1);
	quick_sort(t,mid+1,high);
	
} 


//归并函数--------------------------------------------- 
void merge(vector<int> &arr, int left, int mid, int right) {
    vector<int> tmp(right-left+1); // 开辟新的数组
    int i = left, j = mid+1, k = 0; // i左边数组的起始位置,j右边数组的起始位置,k已经放入新数组的个数

    while (i <= mid && j <= right) {  // 比较左右数组,数值小的放入新数组中
        tmp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
    }
    while (i <= mid) tmp[k++] = arr[i++];  // 如果左半边数组没有排序完,加入新数组
    while (j <= right) tmp[k++] = arr[j++]; // 如果右半边数组没有排序完,加入新数组

    for (i = left, k = 0; i <= right;) arr[i++] = tmp[k++]; // 新数组数据放回到原来的数组中
}

void mergeSort(vector<int> &arr, int left, int right) {
    if (left >= right) return;

    int mid = left + (right - left) / 2;
    mergeSort(arr, left, mid); // 递归 左半边
    mergeSort(arr, mid+1, right); // 递归 右半边
    merge(arr, left, mid, right); // 合并两个有序的数组(参考合并两个有序的链表)
}


//堆排序-----------------------------------------
void heapSort(vector<int> &arr) {
    priority_queue<int,vector<int>,greater<int>> q; //小顶堆

    for(int i = 0; i < arr.size(); i++) {
        q.push(arr[i]);
    }
    for (int i = 0; i < arr.size(); i++) {
        arr[i] = q.top();
        q.pop();
    }
} 

//上面的是调用了库函数 
void shiftDown(vector<int> &t, int k, int len) {
	int i=k,j=i*2;
    while(j<=len)
    {
        if(j+1<=len&&t[j+1]>t[j])
        {
            j = j+1;
        }
        if(t[j]>t[i])
        {
            swap(t[i],t[j]);
            i=j;
            j=i*2;
        }
        else
            break;
    }
}

void Build_Max_Heap(vector<int> &t, int len) {
	for(int i=len/2; i>=0; --i) {
		shiftDown(t, i, len);
	}
}

void hand_heapSort(vector<int>& t){
	Build_Max_Heap(t,t.size()-1);//初始堆 
	for(int i=t.size()-1; i>=1; --i) {
		swap(t[i],t[0]); //把堆顶元素和堆低元素交换 
		shiftDown(t, 0, i-1);//调整 
	}
} 

 
int main()
{
	cout<<"此排序demo不仅本地能通过,还得要能通过牛客网的测试"<<endl; 
	cout<<endl; 
	vector<int> res{49, 36, 24, 65, 97, 6, 25};
	vector<int> res2{49, 36, 24, 65, 97, 6, 25};
	vector<int> res3{49, 36, 24, 65, 97, 6, 25};
	
	cout<<"归并排序:"; 
	mergeSort(res, 0, res.size()-1);
	for(int i=0;i<res.size();i++)
		cout<<res[i]<<" ";
	cout<<endl;
	
	cout<<"快速排序:"; 
	quick_sort(res2, 0, res2.size()-1);
	for(int i=0;i<res2.size();i++)
		cout<<res2[i]<<" ";
	cout<<endl;
	
	cout<<"堆排序:"; 
	hand_heapSort(res3);
//	heapSort(res3);
	for(int i=0;i<res3.size();i++)
		cout<<res3[i]<<" ";
	cout<<endl;
	
	return 0;
}

在这里插入图片描述

✅快速排序

✅归并排序

✅堆排序

1、奇数移动到偶数前面,且保持相对顺序不变(×)

思路:这题要求相对位置保持不变不就是在要求算法稳定性吗
快速排序是不稳定算法,若不要求保持相对顺序的话,可以用吉大的快排

吉大快排:
		int i=0,j=nums.size()-1;
        while(i<=j){
            while(i<=j&&nums[i]%2==1)
                i++;
            while(i<=j&&nums[j]%2==0)
                j--;
            if(i<=j){
                swap(nums[i],nums[j]);
                i++;
                j--;
            }
        }
        return nums;



class Solution {
public:
    
    vector<int> reOrderArray(vector<int>& array) {
        // write code here
        /**
       1.冒泡排序,空间复杂度为O(1)
        2.空间复杂度O(n)
        3.相对位置可以改变可以用快速排序
     */
        int flag = true;
        while (flag) {
            flag = false;
            for (int i = 1; i < array.size(); ++i) {
                if(array[i-1]%2==0 && array[i]%2){
                    flag = true;
                    swap(array[i-1],array[i]);
                }
            }
        }
        return array;
    }
};

堆排序:

1、出现次数的topK问题(❌)

添加链接描述

#include<unordered_map>
class Compare
{
public:
    bool operator()(const pair<int, string>& a, const pair<int, string>& b) const
    {
        if(a.first == b.first) return a.second<b.second;
        return a.first > b.first;
    }
};

class Solution {
public:
    /**
     * return topK string
     * @param strings string字符串vector strings
     * @param k int整型 the k
     * @return string字符串vector<vector<>>
     */
    vector<vector<string> > topKstrings(vector<string>& strings, int k) {
        //if(strings.empty() || k<=0) return {};
        
        unordered_map<string, int> mp;
        for(auto str : strings) ++mp[str];
        
        priority_queue< pair<int, string>, vector<pair<int, string>>, Compare > q;
        
        auto it = mp.begin();
        for(int i=0; i<k&&it!=mp.end(); ++i)
        {
            q.o(it->second, it->first);
            ++it;
        }
        
        while(it!=mp.end())
        {
            if(it->second > q.top().first || (it->second==q.top().first && it->first<q.top().second))
            {
                q.pop();
                q.emplace(it->second, it->first);
            }
            
            ++it;
        }
        
        vector<vector<string>> ret;
        while(!q.empty())
        {
            ret.push_back({q.top().second, to_string(q.top().first)});
            q.pop();
        }
        reverse(ret.begin(), ret.end());
        
        return ret;
    }
};

2、数组中的前k个数(✅)

堆排序出入都是从根执行的
C++默认是大根堆
//升序队列,小顶堆
2 priority_queue <int,vector<int>,greater<int> > q;
3 //降序队列,大顶堆
4 priority_queue <int,vector<int>,less<int> >q;
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> t;
        if(input.size()<k)
            return t;
        priority_queue<int ,vector<int>,greater<int>> q;//小根堆
        for(auto c:input)
            q.push(c);
        while(k--){
            t.push_back(q.top());
            q.pop();
        }
        return t;
    }

3、数组中找第K大的数(❌)要用小根堆?

这题二分查找暂时还不会
class Solution {
public:
    int findKth(vector<int> a, int n, int k) {
        // write code here
        priority_queue<int, vector<int>, greater<int>> q;
        for(auto c:a){
            q.push(c);//这里是先进堆后出堆
            if(q.size()>k)
                q.pop();
        }
        return q.top();
    }
};

五、二分

1、有重复数字的升序数组的二分查找(×)

添加链接描述

对于:1 2 2 3 4,target = 2,返回的下标是1
下面这个是要返回第一个重复数字的下标

结合下面第三题的改良版

就是找下界
int search(vector<int>& nums, int target) {
        // write code here
        int l=0,r=nums.size();
        while(l<r){
            int mid = l+(r-l)/2;
            if(nums[mid]<target)
                l = mid+1;              
            else
                r = mid;
        }
        return (l<nums.size() && nums[l]==target)?l:-1;
    }

2、排序数组中的缺失数字(×)

在这里插入图片描述

	int missingNumber(vector<int>& nums) {
        int i = 0;
        int j = nums.size() - 1;
        while(i <= j){//这里必须要等于
            int mid = (i + j)/2;
            if(nums[mid] == mid){ // mid之前的元素位置正确 
                i = mid + 1; //查找后面的元素情况
            }else{ //mid之后的元素位置正确
                j = mid - 1; //查找前面的元素情况
            }
        }
    	return i;
    }

3、数字在升序数组中出现的次数(×)

1、暴力遍历
时间复杂度:O(N)
空间复杂度:O(1)

2、二分
时间复杂度:O(logN)
空间复杂度:O(1)

先查找目标范围的下界和上界。
下界定义为:如果存在目标值,则指向第一个目标值,否则,如果不存在, 则指向大于目标值的第一个值。
上界定义为:不管目标值存在与否,都指向大于目标值的第一个值。

class Solution {
public:
    int GetNumberOfK(vector<int> nums ,int target) {
        int lbound = 0, rbound = 0;
        // 寻找上界
        int l = 0, r = nums.size();
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] < target) {
                l = mid + 1;
            }
            else {
                r = mid;
            }
        }
        lbound = l;
        // 寻找下界
        l = 0, r = nums.size();
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] <= target) {
                l = mid + 1;
            }
            else {
                r = mid;
            }
        }
        rbound = l;
        return rbound - lbound;
    }
};

4、旋转有序数组的最小整数(×)

二分
时间复杂度:所以为O(longN), 但是如果是[1, 1, 1, 1],会退化到O(n)
空间复杂度:没有开辟额外空间,为O(1)

class Solution {
public:
    int minNumberInRotateArray(vector<int> nums) {
        if(nums.size()==0)
            return 0;
        int l=0,r=nums.size()-1;
        while(l<r){
            if(nums[l]<nums[r])
                return nums[l];
            int mid = l + (r-l)/2;
            if(nums[mid]>nums[r])
                l = mid+1;
            else if(nums[mid]<nums[r])
                r = mid;
            else
                r--;
        }
        return nums[l];
    }
};

5、有转动过的有序数组中寻找目标值(❌)

	int search(int* A, int n, int target) {
        // write code here
        int left = 0, right = n - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (A[mid] == target) return mid;
            if (A[mid] >= A[left]) {
                // 左侧有序(含A[mid])
                if (A[mid] > target && A[left] <= target)//注意这里要等于
                    right = mid - 1;
                else
                    left = mid + 1;
            } else {
                // 右侧有序(含A[mid])
                if (A[mid] < target && A[right] >= target)//注意这里要等于
                    left = mid + 1;
                else
                    right = mid - 1;
            }
        }
        return -1;
    }

六、dp

1、丑数(×)

在这里插入图片描述

思想:三行代表三个数组存放2,35的倍数,遍历三个数组找最小
1*2	2*2 3*2
1*3 2*3 3*3
1*5 2*5 3*5
int GetUglyNumber_Solution(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1;
        int p2=0,p3=0,p5=0;
        for(int i=1;i<=n;i++){//注意这里
            dp[i] = min(min(dp[p2]*2,dp[p3]*3),dp[p5]*5);
            if(dp[i]==dp[p2]*2)
                p2++;
            if(dp[i]==dp[p3]*3)
                p3++;
            if(dp[i]==dp[p5]*5)
                p5++;
        }
        return dp[n-1];//1是第一个丑数,所以返回n-1
    }

2、最长回文子串(×)

class Solution {
public:
    int getLongestPalindrome(string A, int n) {
        if(n <= 1) return n;
        int longest = 1;
        bool dp[n][n];
        for(int i = 0; i < n; i++) { //从短到长对每种长度分别判断,可以这么做是因为判断较长的需要利用较短的
            for(int j = 0; j< n - i; j++) { //从头开始对长度i+1的子字符串判断
                if(i == 0) dp[j][j] = 1; //长度1一定为回文
                else if(i == 1) dp[j][j + 1] = (A[j] == A[j + 1]); //长度2判断头尾是否相等
                else if(A[j] == A[j + i]) dp[j][j + i] = dp[j + 1][j + i - 1]; //长度大于等于3,判断两头是否相等,若相等则同去两头的bool值一样
                else dp[j][j + i] = 0; //否则为0
                if(dp[j][j + i]) longest = max(longest, i + 1); //更新最大值
            }
        }
        return longest;
    }
};

3、最长公共子串(×)

动态规划
string LCS(string str1, string str2) {
        // write code here
        int m = str1.size();
        int n = str2.size();
        // dp[i][j] str1前i个字符和str2前j个字符(以其为尾字符)的最长公共子串长度
        int dp[m+1][n+1];
        int maxlen = 0, end = 0;
        //base case
        for(int i = 0; i <= m; ++i) dp[i][0] = 0;
        for(int j = 0; j <= n; ++j) dp[0][j] = 0;
        for(int i = 1; i <= m; ++i) {
            for(int j = 1; j <= n; ++j) {
                if(str1[i-1] == str2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;//注意这里(+1)
                else dp[i][j] = 0;
                if(dp[i][j] > maxlen) {//注意这个if
                    maxlen = dp[i][j];
                    end = j - 1;
                }
            }
        }
        string r;
        if(maxlen == 0) return "-1";
        else r = str2.substr(end-maxlen+1, maxlen);
        return r;
    }

4、青蛙跳台阶(✅)

class Solution {
public:
    int numWays(int n) {
        vector<int> dp(n+1,1);
        for(int i=2;i<=n;i++){
            dp[i] = (dp[i-1] + dp[i-2])%1000000007;
        }
        return dp[n];
    }
};

七、hashmap

1、两数之和(✅)

map+target-nums[i]
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // write code here
        unordered_map<int,int> m;
        for(int i=0;i<nums.size();i++){
            auto it = m.find(target-nums[i]);
            if(it!=m.end())
                return {it->second+1,i+1};//返回的数据顺序会有影响
            m[nums[i]] = i;//别忘记写了
        }
        return {};
    }
};

2、三数之和(✅)

完整代码
//三数之和
#include<iostream>
#include<algorithm> 
#include<vector>
#include<map>
using namespace std;

vector<vector<int>> test1(vector<int> &nums){
	//双指针做法,时间复杂度O(n^2) 
	vector<vector<int>> res;
	sort(nums.begin(),nums.end());
	for(int i=0;i<nums.size();i++){
		if(nums[0]>0)
			break;
		if(i>0&&nums[i-1]==nums[i])
			continue;
		int l=i+1,r=nums.size();
		while(l<r){
			int s = nums[i] + nums[l] + nums[r];
			if(s==0){
				res.push_back({nums[i],nums[l],nums[r]});
				while(l<r&&nums[l++]==nums[r]);
				while(l<r&&nums[r--]==nums[r]);
			}
			else if(s>0)
				r--;
			else
				l++;
		}
	}
	return res;
}


vector<vector<int>> test2(vector<int>& nums){
	//下面这是时哈希表做法 ,时间复杂度O(n^2) 
	vector<vector<int>> res;
	sort(nums.begin(),nums.end()) ;
	map<int,int> m;
	for(int i=0;i<nums.size();i++){
		m[nums[i]] = i;
	} 
	for(int i=0;i<nums.size();i++){
		if(nums[0]>0)
			break;
		if(i>0&&nums[i-1]==nums[i])
			continue;
//		map<int,int> m;//为啥map要放在循环体中 
		for(int j=i+1;j<nums.size();j++){
			if(m.count(-nums[i]-nums[j])){
				res.push_back({nums[i],nums[j],-nums[i]-nums[j]});
				while(j+1<nums.size()&&nums[j]==nums[j++]);
			}	
//			else
//				m[nums[j]] = j;	//为啥要这么赋值	
		}
	}
	return res;
}

bool test3(vector<int>& nums){
	//字节问的是存在三数之和返回true即可
	sort(nums.begin(),nums.end());
	for(int i=0;i<nums.size();i++){
		if(nums[0]>0)
			return false;
		int l=i+1,r=nums.size();
		while(l<r){
			int s = nums[i]+nums[l]+nums[r];
			if(s==0)
				return true;
			else if(s>0)
				r--;
			else
				l++;
		} 
	}
	return false; 
}

//之前理解不行,下面这个是暂时的最终版本 
bool test4(vector<int>& nums){
	sort(nums.begin(),nums.end());
	map<int,int> m;
	for(int i=0;i<nums.size();i++){
		m[nums[i]] = i;
	} 
	for(int i=0;i<nums.size();i++){
//		map<int,int> m;//每次都要新建一个map ,浪费 
		for(int j=i+1;j<nums.size();j++){
			//if(m.find(-nums[i]-nums[j])!=m.end())
			if(m.count(-nums[i]-nums[j]))
				return true;
//			else
//				m[nums[j]] = j;//我丢,那天被绕进入了 
		}
	}
	return false; 
} 

int main(){
	vector<int> a{2,0,-1,1,-1,-2};
	vector<int> b{-1,-1,-1,2};
	vector<vector<int> > res;
//	b = test1(a);
	res = test2(a);
	
//	bool flag = test3(a);//双指针思路 
	bool flag = test4(b);//哈希思路 
	if(flag == true)
		cout<<"yes"<<endl;
	else
		cout<<"No"<<endl;
	
	for(int i=0;i<res.size();i++){
		for(int j=0;j<res[0].size();j++){
			cout<<res[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
} 

在这里插入图片描述

结合上面注意下面的哈希会出现的问题,输出数组不够
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int len = nums.size();
        vector<vector<int>> ans;

        sort(nums.begin(), nums.end());
        for (int i = 0; i < len; i++) {
            if (i != 0 && nums[i] == nums[i - 1]) continue; // 跳过i代表的重复数字
            unordered_map<int, int> m;
            for (int j = i + 1; j < len; j++) {
                if (m.count(-nums[i] - nums[j])) {
                    ans.push_back({ nums[i], nums[j], -nums[i] - nums[j] });
                    while (j + 1 < len && nums[j] == nums[j + 1]) j++; // 跳过j代表的重复数字
                }
                else m.insert(pair<int, int>(nums[j], j)); // 我们是在nums[i]后面的数字寻找nums[j]和nums[k](即-nums[i] - nums[j]),所以这里存储nums[j]
            }
        }
        
        return ans;
    }
};
下面这个三数之和是判断是否为0
排序+去重
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        for(int i=0;i<nums.size();i++){
            if(nums[0]>0)
                break;
            if(i>0&&nums[i-1]==nums[i])//去重
                continue;
            int l=i+1,r=nums.size()-1;
            while(l<r){
                int s = nums[i]+nums[l]+nums[r];
                if(s==0){
                    res.push_back({nums[i],nums[l],nums[r]});
                    while(l<r&&nums[r--]==nums[r]);//去重
                    while(l<r&&nums[l++]==nums[l]);
                }
                else if(s>0)
                    r--;
                else
                    l++;
            }
        }
        return res;
    }
};

八、位运算

1、二进制中1的个数(✅)

例:A = 0011 1100,A >> 2 将得到 15,即为 0000 1111
class Solution {
public:
     int  NumberOf1(int n) {
         int t = 0;
         for(int i=0;i<32;i++){
             if(n>>i&1)
                 t++;
         }
         return t;
     }
};

九、DFS

LRU算法(❌)

最近最久未使用算法(OS页面置换算法中的一种),(缓存算法)

下面这个方法用到了很多库函数
#include <unordered_map>
#include <list>
#include <vector>
using namespace std;
struct Node
{
    Node(int k = 0, int v = 0) : key(k), val(v) {}
    int key;
    int val;
};
class Solution
{
public:
    /**
     * lru design
     * @param operators int整型vector<vector<>> the ops
     * @param k int整型 the k
     * @return int整型vector
     */
    vector<int> LRU(vector<vector<int>> &operators, int k)
    {
        // write code here
        cap = k;
        vector<int> ans;
        for (auto &input : operators)
        {
            if (input[0] == 1)
            {
                set(input[1], input[2]);
            }
            else
            {
                ans.push_back(get(input[1]));
            }
        }
        return ans;
    }
    //删除
    int remove(std::list<Node>::iterator &ite)
    {
        int key = ite->key;
        int val = ite->val;
        L.erase(ite);
        H.erase(key);
        return val;
    }
    // 添加
    void add(int key, int val)
    {
        L.push_front(Node(key, val));
        H[key] = L.begin();
        if (L.size() > cap)
        {
            auto last = L.end();
            --last;
            remove(last);
        }
    }
    void set(int x, int y)
    {
        auto ite = H.find(x);
        //已经存在,删除了再添加到头部
        if (ite != H.end())
        {
            remove(ite->second);
        }
        add(x, y);
    }
    int get(int x)
    {
        int val = 0;
        //已经存在,删除了再添加到头部
        auto ite = H.find(x);
        if (ite != H.end())
        {
            val = remove(ite->second);
            add(x, val);
            return val;
        }
        return -1;
    }

private:
    std::list<Node> L;
    std::unordered_map<int, std::list<Node>::iterator> H;
    int cap;
};

开始初始化一个head节点与一个tail节点,方便以后插入节点和删除节点,中间放置操作的节点。

在这里插入图片描述

[1,1,1] 当我们遇到第一个set方法的时候 就需要插入到head 和tail 之间,
在这里插入图片描述

[1,2,2] 这时我们需要将新节点插入到head与node(1,1)之间。

在这里插入图片描述

[1,3,2] 添加到head后面;
在这里插入图片描述

[2,1] 发现已经有key=1对应的节点;则把Node(1,1)移动到head后面;
在这里插入图片描述

[1,4,4] 这时候发现节点的数量已经达到内存上限,则需要把最不常用的节点Node(2,2)删除,把新节点插入到head后面;
在这里插入图片描述

[2,2] 这时候查找节点发现没有,则返回-1;

还是用下面的这个方法吧,比较原生
#include <unordered_map>

struct DListNode{
    int key, val;
    DListNode* pre;
    DListNode* next;
    DListNode(int k, int v): key(k), val(v), pre(nullptr), next(nullptr){};
};


class Solution {
private:
    int size = 0;
    DListNode* head;
    DListNode* tail;
    unordered_map<int, DListNode*> mp;

public:
    /**
     * lru design
     * @param operators int整型vector<vector<>> the ops
     * @param k int整型 the k
     * @return int整型vector
     */
    vector<int> LRU(vector<vector<int> >& operators, int k) {
        // write code here
        if(k < 1) return {};
        this->size = k;
        this->head = new DListNode(0,0);
        this->tail = new DListNode(0,0);
        this->head->next = this->tail;
        this->tail->pre = this->head;

        if(operators.size() == 0) return {};

        vector<int> res;

        for(vector<int> op : operators){
            if(op[0] == 1) {
                set(op[1], op[2]);
            }
            else if(op[0] == 2){
                int value = get(op[1]);
                res.push_back(value);
            }
        }
        return res;
    }

    void set(int key, int val){
        if(mp.find(key) == mp.end()){ // hashmap 中没找到
            DListNode* node = new DListNode(key, val);
            mp[key] = node;
            if(this->size <= 0){
                removeLast();
            }
            else{
                this->size--;
            }
            insertFirst(node);
        }
        else{  // hashmap 中已经有了,也就是链表里也已经有了
            mp[key]->val = val;
            moveToHead(mp[key]);
        }
    }


    int get(int key){
        int ret = -1;
        if(mp.find(key) != mp.end()){
            ret = mp[key]->val;
            moveToHead(mp[key]);
        }
        return ret;
    }

    void moveToHead(DListNode* node){
        if(node->pre == this->head) return;
        node->pre->next = node->next;
        node->next->pre = node->pre;
        insertFirst(node);
    }


    void removeLast(){
        mp.erase(this->tail->pre->key);
        this->tail->pre->pre->next = this->tail; // remove the last node in dlist
        this->tail->pre = this->tail->pre->pre;
    }

    void insertFirst(DListNode* node){
        node->pre = this->head;
        node->next = this->head->next;
        this->head->next->pre = node;
        this->head->next = node;
    }

};

洗牌算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值