【每日刷题3.3】10道算法+10道面试 - 阿V

大三菜鸟一枚,经历过几次笔试,全部挂了,甚至有些连简历都没过,继续努力,打好基础,早日拿到offer!!!

算法题(牛客网)

1.反转链表 

空间复杂度O(1),意味着以下方案都不可行

1.创建vector数组存储所有值,接着反转数组,再依次修改原本链表的值。

2.递归到最后一个节点,用一个新的链表进行反向进行链接。


所以能否使用几个常量来进行反转,很容易就想到用双指针(我学过哈哈哈)。用两个指针,分别指左边和右边,一步一步反转过来就可以

 详细代码:

非递归版本:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode* left = nullptr,* right = pHead; //左指针指向nullptr,右指针指向头节点
        while(pHead){
            right = pHead->next; //右指针保存下一节点
            pHead->next = left; //头指针指向左节点
            left = pHead; //左指针更新为头指针
            pHead = right; //头指针更新为右指针
        }
        
        return left; //左指针就是新的头指针
    }
};

 递归版本:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        return Reverse(pHead);
    }
    
    
    ListNode* Reverse(ListNode* right,ListNode* left = nullptr){ //创建两个指针分别指向左边和右边
        if (!right){
            return left; //当右边为空时返回左指针,因为左指针是新的头指针
        }
        ListNode* tem_right = right->next; //保存右边的下一节点
        right->next = left; //右边的头指针指向左指针
        return Reverse(tem_right,right); //将新的右指针和左指针传入
    }
};

 2.排序

 要求时间复杂度O(n2),空间复杂度O(n),可以采用以下方法:

 百度了一圈,好像没有空间复杂度O(n),时间复杂度O(n2)的哈哈哈,正常来说使用:冒泡、快排等。


我们肯定要目标更加远大,直接进阶要求:时间复杂度O(nlogn),就是归并排序!!!

归并排序就是典型的分治思想,将问题分成诺干个小问题,最后再合并在一起!

(这里使用一下他人的图牛客@数据结构和算法)

代码详情:

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型vector 待排序的数组
     * @return int整型vector
     */
    vector<int> MySort(vector<int>& arr) {
        // write code here
        mergeSort(arr, 0, arr.size()-1); //传入分割函数,将数组分成一块块
        return arr;
    }
    
    
    void mergeSort(vector<int>& arr,int l,int r){ //分割函数
        if(l==r) return;
        int mid = (l+r)/2;
        mergeSort(arr, l, mid); 
        mergeSort(arr, mid + 1, r); //对半分
        
        merge(arr, l, mid, r); //合并函数
    }
    
    
    void merge(vector<int>& arr,int l,int mid, int r){ //合并函数
        vector<int> help = vector<int>(r-l+1);
        int i = 0;
        int p1 = l,p2 = mid + 1;
        
        for(;p1 <= mid && p2 <= r;++i){
            help[i] = arr[p1] < arr[p2]?arr[p1++]:arr[p2++]; //将两个数组按顺序合起来
        }
        while(p1 <= mid){
            help[i++] = arr[p1++]; //如果第一个数组还没遍历完则全部放入数组后面
        }
        while(p2 <= r){
            help[i++] = arr[p2++]; //同理
        } 
        
        for(i=0;i<r-l+1;++i){
            arr[l+i] = help[i]; //将合并好的数组复制给arr
        }
    }
};

3.设计LRU缓存结构


由于要求操作复杂度都为O(1),整体采用哈希双链表指针(我网上学的哈哈哈)。这道题对刚学的我来说挺困难的,耗费了很多时间,不过了解了LRU算法的原理。

 代码详情:

struct DListNode{ //自定义数据结构
    int key,val; //保存key 和 value
    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 || operators.size() == 0) return {}; //当缓存结构为0或无缓存时返回null
        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; //以上初始化和构建空双向链表
        
        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.count(key)){ //哈希表不存在当前存入数据,将数据存入缓存
            DListNode* node = new DListNode(key,val);
            mp[key] = node;
            if(this->size <= 0){ // 如果缓存结构不足,则删除最近最少使用数据
                removeLast();
            }
            else{
                this->size--; //缓存结构充足,内存减一
            }
            insertFirst(node); //插入新数据
        }
        else{ //哈希表存在数据,则将数据更新到头部
            mp[key]->val = val; 
            moveToHead(mp[key]);
        }
    }
    
    int get(int key){ //获取数据
        int ret = -1; //如果不存在,返回-1
        if(mp.count(key)){ //哈希表存在,将数据更新直头部
            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); //将哈希表中的数据删除
        DListNode* tem = this->tail->pre;
        this->tail->pre->pre->next = this->tail;
        this->tail->pre = this->tail->pre->pre;
        delete tem;
    }
    
    void insertFirst(DListNode* node){ //将数据插入头部
        node->pre = this->head;
        node->next = this->head->next;
        this->head->next->pre = node;
        this->head->next = node;
    }
};

 4/5/6. 实现二叉树先序,中序和后序遍历

 居然一题同时实现三种遍历太过分了!把它当作3道题不过分吧,而且我是递归和非递归两种版本都实现,这是基础,不用百度哈哈哈。


代码详情:

递归版本:

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 the root of binary tree
     * @return int整型vector<vector<>>
     */
    vector<vector<int> > threeOrders(TreeNode* root) {
        // write code here
        vector<vector<int>> res; //存储最终答案
        vector<int> ans; //存储每次遍历数据
        
        preTraversal(root, ans); //先序遍历
        res.push_back(ans); //存储先序遍历数据
        
        ans = vector<int>(); //清空ans
        midTraversal(root, ans); //同理
        res.push_back(ans);
        
        ans = vector<int>();
        postTraversal(root, ans);
        res.push_back(ans);
        
        return res;
    }
    
    //先序遍历递归
    void preTraversal(TreeNode* root,vector<int>& ans){
        if (!root) return;
        ans.push_back(root->val);
        preTraversal(root->left, ans);
        preTraversal(root->right, ans);
        return;
    }
    
    //中序遍历递归
    void midTraversal(TreeNode* root,vector<int>& ans){
        if (!root) return;
        midTraversal(root->left, ans);
        ans.push_back(root->val);
        midTraversal(root->right, ans);
        return;
    }
    
    //后序遍历递归
    void postTraversal(TreeNode* root,vector<int>& ans){
        if (!root) return;
        postTraversal(root->left, ans);
        postTraversal(root->right, ans);
        ans.push_back(root->val);
        return;
    }
};

非递归版本:

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
struct postTreeNode{ //后序遍历专用结构体
    int flag = 0; //记录是否遍历左右节点,0表示未遍历,1表示遍历了左节点,2表示遍历了右节点
    TreeNode* node;
    
    postTreeNode(TreeNode* node):flag(0),node(node){};
};


class Solution {
public:
    /**
     * 
     * @param root TreeNode类 the root of binary tree
     * @return int整型vector<vector<>>
     */
    vector<vector<int>> threeOrders(TreeNode* root) {
        // write code here
        vector<vector<int>> res;
        res.push_back(un_preTraversal(root)); //保存先序遍历数据
        res.push_back(un_midTraversal(root));
        res.push_back(un_postTraversal(root));
        
        return res;
    }
    
    //非递归先序遍历,使用栈进行遍历
    vector<int> un_preTraversal(TreeNode* root){
        vector<int> ans; //存储数据
        stack<TreeNode*> stack; //保存未遍历结束的节点
        
        while(root || !stack.empty()){
            if (root){
                ans.push_back(root->val); //保存节点
                stack.push(root->right);
                root = root->left;
            }
            else{
                root = stack.top();
                stack.pop();
            }
        }
        
        return ans;
    }
    
    //非递归中序遍历
    vector<int> un_midTraversal(TreeNode* root){
        vector<int> ans;
        stack<TreeNode*> stack;
        
        while(root || !stack.empty()){
            if (root){
                stack.push(root); //一开始不存储数据先保存节点
                root = root->left;
            }
            else{
                root = stack.top();
                stack.pop();
                ans.push_back(root->val); //第二次遍历到才存储数据
                root = root->right;
            }
        }
        
        return ans;
    }
    
    //非递归后序遍历
    vector<int> un_postTraversal(TreeNode* root){
        vector<int> ans;
        stack<postTreeNode*> stack; //由于要记录根节点是否遍历了左右节点,要新建结构体
        postTreeNode* node = new postTreeNode(root);//创建后序遍历专用节点
        
        while(node->node || !stack.empty()){
            if (node->node){
                if (node->flag == 0){ //未遍历左右节点
                    node->flag = 1;
                    stack.push(node);
                    root = root->left;
                    node = new postTreeNode(root);
                }
                else if(node->flag == 1){ //未遍历右节点
                    node->flag = 2;
                    stack.push(node);
                    root = root->right;
                    node = new postTreeNode(root);
                }
                else if (node->flag == 2){ //两边都遍历,可以保存数据
                    ans.push_back(node->node->val);
                    node->node = nullptr; //不直接使用stack.top(),防止非法访问
                }
            }
            else{
                node = stack.top();
                stack.pop();
                root = node->node;
            }
        }
        
        return ans;
    }
};

7. 最小的K个数

 看到要求空间复杂度O(n),时间复杂度O(nlogn),直接想到归并排序,排序完后输出前K个就能解决。但我还是觉得它想考我堆排。


代码详情:

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if (input.size() == 0 || k <= 0) return {};
        mergesort(input, 0, input.size()-1);
        return vector<int>({input.begin(),input.begin()+k});
    }
    
    //分割函数
    void mergesort(vector<int>& input,int l,int r){
        if (l >= r){
            return;
        }
        int mid = (l+r)/2;
        mergesort(input,l,mid); //平均分割成左右两部分
        mergesort(input, mid+1, r);
        
        merge(input, l, mid,r); //合并数组
    }
    
    //合并函数
    void merge(vector<int>& input,int l,int mid,int r){
        vector<int> ans = vector<int>(r-l+1);
        int i = 0; //记录新数据下标
        int p1=l,p2=mid+1;
        
        while(p1<=mid && p2<=r){
            ans[i++] = input[p1] < input[p2]?input[p1++]:input[p2++];//保存小的数据
        }
        
        while(p1<=mid){ //判断左右哪个部分还有剩下的,保存剩下的数据
            ans[i++] = input[p1++];
        }
        while(p2<=r){
            ans[i++] = input[p2++];
        }
        
        i=0;
        while(i<r-l+1){ //复制回原数组
            input[l+i] = ans[i];
            ++i;
        }
    }
};

麻了麻了,没想到第一天不能实现每天10道笔试,10道面试,明天一定努力完成!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZW游戏制造

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值