秋招/春招常见笔试题目——链表系列(C/C++)

大家好!下面是我(一个小小的搬运工)在秋招的时候在Leetcode上整理的一些链表的题目(中等难度),笔试和面试考相似思路题目的概率比较大,大家如果准备春秋季招聘可以先根据这些题目复习(具体思路可以看Leetcode中的讲解——困难的题有链接)://// 定义链表结构struct ListNode { int val; ListNode *next; ListN...
摘要由CSDN通过智能技术生成

        大家好!下面是我(一个小小的搬运工)在秋招的时候在Leetcode上整理的一些链表的题目(中等难度),笔试和面试考相似思路题目的概率比较大,大家如果准备春秋季招聘可以先根据这些题目复习(具体思路可以看Leetcode中的讲解——困难的题有链接):


   定义链表结构
struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
	
};

//2019_05_03

//1、反转指定区间的链表顺序   
//Given1->2->3->4->5->NULL, m = 2 and n = 4,
//return1->4->3->2->5->NULL
/*
对于reverse部分有点迷糊。网上看到的解释,也许更能帮助理解.https://yq.aliyun.com/articles/3867
不妨拿出四本书,摞成一摞(自上而下为 A B C D),要让这四本书的位置完全颠倒过来(即自上而下为 D C B A):
盯住书A,每次操作把A下面的那本书放到最上面
初始位置:自上而下为 A B C D
第一次操作后:自上而下为 B A C D
第二次操作后:自上而下为 C B A D
第三次操作后:自上而下为 D C B A
*/
class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        ListNode *dummy = new ListNode(0);
        dummy->next = head;
        ListNode *preStart = dummy;
        ListNode *start = head;
        for(int i = 1; i < m; i++)
        {
            preStart = start;
            start = start->next;
        }
         
        for(int i = 0; i < n - m; i++)
        {
            ListNode *tmp = start->next;
            start->next = tmp->next;
            tmp->next = preStart->next;
            preStart->next = tmp;
        }
         
        return dummy->next;
    }
};

//2019_05_05
//1、将两个已经排序的链表合并起来
/*
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
        ListNode *head = new ListNode(0);
        ListNode *t = head;
        while (l1 != NULL || l2 != NULL) {
            if (l1 == NULL) {
                t->next = l2;
                l2 = l2->next;
            }
            else if (l2 == NULL) {
                t->next = l1;
                l1 = l1->next;
            }
            else if (l1->val < l2 -> val){
                t->next = l1;
                l1 = l1->next;
            }
            else {
                t->next = l2;
                l2 = l2->next;
            }
            t = t->next;
        }
        return head->next;
    }
};

//2019_05_23
//构建一个LRU(最近最少使用)缓存机制


LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
/*
cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


核心思想就是构建出带有表头和表尾的双链表,并用一个map存储所有节点,更新和添加
时将节点放在双链表头部,超限时删除表尾。获取时直接从map中获取节点值。

//定义三个private数据,LRU尺寸,LRU pair<key,value>, LRU map<key,iterator of pair>

//利用splice操作erase,makepair 等完成LRUcache

//put()
如果m中有对应的key,value,那么移除l中的key,value
如果m中没有,而size又==cap,那么移除l最后一个key,value,并移除m中对应的key,iterator

在l的开头插入key,value,然后m[key]=l.begin();


*/

class LRUCache {
public:
    LRUCache(int capacity){
        cap=capacity;
    }
    int get(int key){
        unordered_map<int,list<pair<int,int>>::iterator>::iterator it=m.find(key);
        if(it==m.end()) return -1;
        l.splice(l.begin(),l,it->second);
        return it->second->second;
    }
    void put(int key,int value){
        auto it=m.find(key);
        if(it!=m.end()) l.erase(it->second);
        if(l.size()==cap){
            int k=l.rbegin()->first;
            l.pop_back();
            m.erase(k);//map可以根据key值和迭代器值移除,查找
        }
        l.push_front(make_pair(key,value));
        m[key]=l.begin();
    }
    
private:
    int cap;//LRU size
    list<pair<int,int>>l;//pair<key,value>
    unordered_map<int,list<pair<int,int>>::iterator>m;//unordered_map<key,key&value's pair iterator>
};

//2019_06_18
/*
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环
*/
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==nullptr)
            return nullptr;
        if(head->next==nullptr)
            return nullptr;
        ListNode *slow=head;
        ListNode *fast=head->next;
        int count=1;
        
        while(slow!=fast){
            if(fast->next==nullptr||fast->next->next==nullptr)
            return nullptr;
            else{
            slow=slow->next;
            fast=fast->next->next;
            count++;
            }
            
        }
        
        slow=head;
        fast=head;
        while(count--)
            fast=fast->next;
        
        if(slow==fast)
            return slow;
        else{
            
            while(slow!=fast){
                fast=fast->next;
                slow=slow->next;
            }
            return slow;
        }
        
        
    }
};
//2019_07_04
/**
1、给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明 :

你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换

**/
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(k==1)
            return head;
        if(!head||!head->next)
            return head;
        int len=findlen(head);        
        int num=len/k;
        int fin=k-1;
        
        ListNode* pre=new ListNode(-1);
        pre->next=head;
        ListNode* now=head;
        ListNode* next=now->next;
        ListNode* dump=pre;
        
        while(num--){
            fin=k-1;
            while(fin--){
                now->next=next->next;
                next->next=pre->next;
                pre->next=next;
                next=now->next;  
            }
            
            pre=now;
            if(pre->next)
                now=pre->next;
            if(now->next)
                next=now->next;
            
            
        }
        
        return dump->next;
        
    }
    
    int findlen(ListNode* head){
        ListNode* p=head;
        int count=0;
        while(p){
            count++;
            p=p->next; 
        }
        return count;
    }
};

/*
2、设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构。

注意: 允许出现重复元素。

insert(val):向集合中插入元素 val。
remove(val):当 val 存在时,从集合中移除一个 val。
getRandom:从现有集合中随机获取一个元素。每个元素被返回的概率应该与其在集合中的数量呈线性相关。
*/
/*

在 insert(val) 和 remove(val) 都可以使用一个 unordered_map<int,int> 来解决。

但是直接使用 hash 不能够 真正的使用一个 getRandom ,所以我们可以使用一个额外的 数组 q[] 来存储所有的数字,而我们的 unordered_map<int,vector<int> > 来存储每一个数字加入之后他在 q[] 中的下标 idx 当要移除某一个数字的时候,我们取到他的下标值 idx 之后再交换再 q[]的对应位置的数字 ,把需要删除的那个数字放到之后 再 cnt-- 就行了。

而获取一个随机数只需要 rand() %cnt 就能生成一个随机的数据,然后返回一个q[r_idx] 的值就行了

*/

class RandomizedCollection {
    int q[10000] = {};
public:
    unordered_map<int,vector<int> > m;
    int cnt = 0;
    /** Initialize your data structure here. */
    RandomizedCollection() {
        srand(time(0));
    }
    
    /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
    bool insert(int val) {
        q[cnt] = val;
        m[val].push_back(cnt++);
        return m[val].size()<=1;
    }
    
    /** Removes a value from the collection. Returns true if the collection contained the specified element. */
    bool remove(int val) {
        if(m[val].size()){
            int idx = m[val].back();
            m[val].pop_back();
            int back = q[cnt-1];
            
            for(int & n: m[back]){
                if(n==cnt-1){
                    n = idx;
                    q[idx] = back;
                    break;
                }
            }
            cnt--;
            return true;
        }
        return false;
    }
    
    /** Get a random element from the collection. */
    int getRandom() {
        int ridx = rand()%(cnt);
        return q[ridx];
    }
};

//2019_07_09
/*
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

*/
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len=lists.size();
        if(len==0)
            return nullptr;
        if(len==1)
            return lists[0];
        
        return mergesort(lists,0,len-1);
    }
    
    ListNode* mergesort(vector<ListNode*>& lists,int start,int end)
    {
        if(start==end)
            return lists[start];
        int mid=(start+end)/2;
        
        ListNode* L1=mergesort(lists,start,mid);
        ListNode* L2=mergesort(lists,mid+1,end);
       
        return merge(L1,L2);
    }
    
    ListNode* merge(ListNode* L1,ListNode* L2)
    {
        if(L1==nullptr)return L2;
        if(L2==nullptr)return L1;
        
        if(L1->val<L2->val)
        {
            L1->next=merge(L1->next,L2);
            return L1;
        }
        else
        {
            L2->next=merge(L1,L2->next);
            return L2; 
                       
        }
  
    }
};


//2019_07_20
/*
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4
*/
class Solution {
public:
    ListNode* findMiddle(ListNode* head){
        ListNode* chaser = head;
        ListNode* runner = head->next;
        while(runner != NULL && runner->next != NULL){
            chaser = chaser->next;
            runner = runner->next->next;
        }
        return chaser;
    }
     
 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL){
            return l2;
        }
        if(l2 == NULL){
            return l1;
        }
        ListNode* dummy = new ListNode(0);
        ListNode* head = dummy;
        while(l1 != NULL && l2 != NULL){
            if(l1->val > l2->val){
                head->next = l2;
                l2 = l2->next;
            }
            else{
                head->next = l1;
                l1 = l1->next;
            }
            head = head->next;
        }
        if(l1 == NULL){
            head ->next = l2;
        }
        if(l2 == NULL){
            head->next = l1;
        }
        return dummy->next;
    }
     
    ListNode* sortList(ListNode* head) {
        if(head == NULL || head ->next == NULL){
            return head;
        }
        ListNode* middle = findMiddle(head);
        ListNode* right = sortList(middle->next);
        middle -> next = NULL;
        ListNode* left = sortList(head);
        return mergeTwoLists(left, right);
    }
};

//2019_07_21
/*
请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false
示例 2:

输入: 1->2->2->1
输出: true


整体思路是先用快慢指针找中点(慢指针的最终位置)
然后再反转中点指向的后半部分链表
前半部分再与后半部分逐元素比较
时间复杂度为O(n)
空间复杂度关键看反转链表是迭代还是递归,我这里用迭代所以是O(1),递归则是O(n)

*/

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(!head||!head->next) return true;   //空链表表或链表只有一个元素
        ListNode *slow=head,*fast=head;       //快慢指针
        for( ; fast&&fast->next ; fast=fast->next->next,slow=slow->next); //快慢指针迭代找中点
        ListNode* back=rev(slow);               //反转后半链表
        for(ListNode* front=head ; front&&back ; front=front->next,back=back->next) 
            if(back->val!=front->val) return false;   //前半链表与后半链表逐元素比较
        return true;
    }
private:
    //反转链表的工具函数
    ListNode* rev(ListNode* head){
        ListNode* pre=new ListNode(-1);
        ListNode* post=nullptr;
        pre->next=head;
        post=head->next;
        
        while(head&&head->next){
            head->next=post->next;
            post->next=pre->next;
            pre->next=post;
            post=head->next;
            
            
        }
        return pre->next;
    }
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值