leetcode系统性刷题(一)-----链表、栈、队列、堆

一.链表
1.反转链表
双指针,使用临时变量储存前驱节点的next。依次反转,返回后继节点curr.

ListNode* reverseList(ListNode* head) {
        if(!head)return NULL;
        ListNode* pre;
        ListNode* curr;
        curr = NULL;
        pre = head;
        while(pre){
            ListNode* tem = pre->next;
            pre->next = curr;
            curr = pre;
            pre = tem;
        }
        return curr;
    }

反转链表||
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

ListNode* reverseBetween(ListNode* head, int m, int n) {
          ListNode* pre = head;
    ListNode* curr = NULL;
    int changenum = n-m+1;
    //1.移动m-1个位置,到达反转的开始节点,并储存到Lt
    while(pre&&--m){
        curr=pre;
        pre=pre->next;
    }
    ListNode* Lt = pre;
    //2.开始反转。反转结束后new_head为反转后链表的头节点,pre为后一个节点
    ListNode* new_head = NULL;
    while(pre&&changenum){
        ListNode* tem = pre->next;
        pre->next = new_head;
        new_head = pre;
        pre = tem;
        changenum--;
    }
    Lt->next=pre;//反转后的尾结点与后一个节点相连
    //3.判断是否从第一个节点开始反转
    if(curr){//不是从第一个节点开始,头结点还是head
        curr->next=new_head;
        return head;
    }
    else{//从第一个节点开始,头结点是new_head
        return new_head;
    }
    }

2.链表相交节点
采用两个链表各自的指针各走一步,走到本链表的尾结点时跳到另外一个链表的头结点,两个指针最终会在交汇点出相遇。总的步数不超过两个链表的节点之和。注意排除空链表的情况。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int length1 = computeLength(headA);
        int length2 = computeLength(headB);
        int step = length1+length2;
        ListNode*head1=headA;
        ListNode*head2=headB;
        if(!head1||!head2)return NULL;
        while(step){
            
            if(head1==NULL){
                head1=headB;
            }
            if(head2==NULL){
                head2=headA;
            }
            if(head1==head2){
                return head1;
            }
           head1=head1->next;
           head2=head2->next;
            step--;
        }
        return NULL;
    }
    int computeLength(ListNode* head){
        int length = 0;
        while(head){
            length++;
            head = head->next;
        }
        return length;
    }

简单写法

ListNode *p=headA;
ListNode *q=headB;
 while(p!=q){
     p==NULL?p=headB:p=p->next;
     q==NULL?q=headA:q=q->next;
 }
 return(p);

3.链表求环
141、142
思路1:使用set查找

bool hasCycle(ListNode *head) {
        std::set<ListNode*> node_set;
        while(head){
            if(node_set.find(head)!=node_set.end()){//在set中找到了head
                return head;
            }
            node_set.insert(head);//将节点插入set
            head = head->next;
        }
        return NULL;
    }

思路2:快慢指针,可以确定环的起点
快指针每次走两步,慢指针每次走一步。
在这里插入图片描述

ListNode *detectCycle(ListNode *head) {
        if(!head)return NULL;
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* meet;

        while(fast&&slow){
            fast = fast->next;
            slow = slow->next;
            if(fast == NULL)return NULL;
            fast = fast->next;
            if(fast == slow){
                meet = fast;//记录下相交点
                break;
            }
        }
        
        while(meet&&head){
            if(meet==head){
                return head;
            }
            meet=meet->next;
            head=head->next;
        }
        return NULL;
    }

4.链表划分(86)
使用两个临时头节点分别存储小于x和大于等于x的节点。
在这里插入图片描述

 ListNode* partition(ListNode* head, int x) {
        ListNode less(0);
        ListNode more(0);
        ListNode* less_ptr = &less;//注意指针与变量之间是引用关系
        ListNode* more_ptr = &more;
        while(head){
            if(head->val < x){
                less_ptr->next = head;
                less_ptr = head;
            }
            if(head->val >= x){
                more_ptr->next = head;
                more_ptr = head;
            }
            head = head->next;
        }
        less_ptr->next = more.next;
        more_ptr->next = NULL;//不要忘记最后添加Null
        return less.next;
    }

5.排序链表合并(21)
同样使用临时头结点及相应的临时指针,遍历两个链表,将较小的量添加到临时节点之后,指针依次后移。

 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode tem_node(0);
        ListNode* tem_ptr = &tem_node;
        ListNode* node1 = l1;
        ListNode* node2 = l2;
        while(node1||node2){
            if(node2&&node1){
                if(node1->val >= node2->val){
                tem_ptr->next = node2;
                tem_ptr = node2;
                node2 = node2->next;
            }
            else {
                tem_ptr->next = node1;
                tem_ptr = node1;
                node1 = node1->next;
            }
            }
            else{
                if(node1 == nullptr &&node2 != nullptr){
                tem_ptr->next = node2;
                tem_ptr = node2;
                node2 = node2->next;

            }
            else if(node1 != nullptr &&node2 == nullptr){
                tem_ptr->next = node1;
                tem_ptr = node1;
                node1 = node1->next;
            }
            }
            
        }
        tem_ptr->next = nullptr;
        return tem_node.next;
    }

代码二:最后哪个有剩余之间将其剩余段链表加到临时指针后就行
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode tem_node(0);
        ListNode* tem_ptr = &tem_node;
        ListNode* node1 = l1;
        ListNode* node2 = l2;
        while(node1&&node2){
           
                if(node1->val >= node2->val){
                tem_ptr->next = node2;
                tem_ptr = node2;
                node2 = node2->next;
                }
            else {
                tem_ptr->next = node1;
                tem_ptr = node1;
                node1 = node1->next;
            }           
            } 
        if(node1){
            tem_ptr->next = node1;
        }
        if(node2){
            tem_ptr->next = node2;
        }
      
        return tem_node.next;
    }

6.合并k个升序链表(23)
注意列表形式的数据的解析处理
注意使用sort函数,比较非int类型的数据,需要自己写compare函数传入。
临时头结点来重新连接加点

用于C++中,对给定区间所有元素进行排序。
头文件是algorithm 时间复杂度为n*log2(n)
Sort函数使用模板: Sort(start,end,排序方法)
用到了快速排序,但不仅仅只用了快速排序,还结合了插入排序和堆排序。
vector、deque,适用sort算法。

使用vector将所有链表节点都push进入,然后按链表val值排序,最后将排序后的重新写成链表。

 static bool com(const ListNode *a, const ListNode *b){
        return a->val < b->val;//从小到大排序
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        vector<ListNode*> vc;
        for(int i=0;i<lists.size();i++){
            ListNode* head = lists[i];
            while(head){
                vc.emplace_back(head);
                head=head->next;
            }
        }
        if(vc.empty()){
            return nullptr;
        }
        std::sort(vc.begin(),vc.end(),com);//传入com函数
        ListNode new_head(0);
        ListNode* new_ptr = &new_head;
        for(int i=0;i<vc.size();i++){
            new_ptr->next=vc[i];
            new_ptr=vc[i];
        }
        new_ptr->next=nullptr;
        return new_head.next;     
    }

7.旋转链表(61)
旋转数k<len时情况正常,k=len时,旋转后的链表和原链表相同,k>len时,计算m=k%len,情况和旋转数k=m时是相同的。
设定两个指针,指向旋转节点的首节点的前一个节点和链表尾结点。找到这两个节点问题就可以解决。

 void length(ListNode* head, int &len, ListNode* &node){
         while(head){
             node = head;
             len++;
             head = head -> next;
         }
         
     }
    ListNode* rotateRight(ListNode* head, int k) {
        ListNode* node = head;
        ListNode* node1 = head;
        int len = 0;
        length(head, len, node);
        if(len == 0) return nullptr;
        int m = k % len;
        if(m==0)return head;
        else{
            for(int i=1;i<len-m;i++){
                node1 = node1->next;
            }
            ListNode* new_head = node1->next;
            node1->next = nullptr;
            node -> next = head;
            return new_head;
        }
    }

8.汇总区间(228)

//拼接形成字符串的函数
 void strcom(int begin, int end, string &str){
        if(begin==end){
                    str = to_string(end);
                }
                else{
                    str = to_string(begin) + "->" + to_string(end);
                }
    }
    vector<string> summaryRanges(vector<int>& nums) {
        vector<string> res;
        if(nums.size()==0)return res;
        int begin = nums[0];
        int end = nums[0];
        string str = "";
        for(int i=1;i<nums.size();i++){
            if(nums[i]==end+1){
                 end = nums[i];
                 continue;
            }
            strcom(begin,end,str);
    	    res.push_back(str);
            begin = nums[i];
            end = nums[i];
        }
        //末尾最后一组添加进result
        strcom(begin,end,str);
        res.push_back(str);
        return res;
    }

9.k个一组,翻转链表(25)
使用双指针的方法写翻转子链表的函数,返回变量为翻转后的链表的头结点和尾结点。

 pair<ListNode*,ListNode*>reverse(ListNode* start, ListNode* end){
        if(start==end){
            return {start,end};
        }
        ListNode* cur = start;
        ListNode* pre = start->next;
        start->next = end->next;//先将start指向end之后的元素
        ListNode* r = end->next;
        while(pre!=r){//注意end->next要提前保存,end->next会随之改变
            ListNode* tem = pre->next;
            pre->next = cur;
            cur = pre;
            pre = tem;
        }
        
        return {cur,start};
    }
   
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(!head||!head->next)return head;
        ListNode pre(0);
        ListNode* prep = &pre;
        prep->next = head;
        ListNode* start, * end, * nex;
        int len = 0;
        while(head){
            len++;
            head = head->next;
        }
        for(int i=0;i<len/k;i++){
            start = prep->next;
            end = start;
            for(int j=1;j<k;j++){
                end = end->next;
            }
            nex = end->next;
            pair<ListNode*,ListNode*>A = reverse(start,end);
            prep->next = A.first;
            A.second->next = nex;
            prep = A.second;
        }
        return pre.next;
        

    }

10.删除倒数第n个节点(19)
思路1:先遍历得出链表长度,然后再遍历到删除节点的前驱结点
思路2:建立一个前驱空洞结点,然后使用快慢指针,慢指针指向空洞结点,快指针指向head结点。先让快指针移动n步,然后再同时移动快慢指针,知道快指针为null。此时慢指针指向删除节点的前驱结点。

ListNode* removeNthFromEnd(ListNode* head, int n) {
        //先建立一个虚拟头结点
        ListNode new_head(0);
        ListNode* ptr = &new_head;
        ptr -> next = head;
        ListNode* curr = ptr;
        ListNode* pre = head;
        //先让pre走n步,
        for(int i=0;i<n;i++){
            pre = pre -> next;
        }
        //同时移动pre和curr,当pre到null时候,pre和curr中间刚好相隔n个节点,curr刚好就是倒数第n个的前驱结点
        while(pre){
            pre = pre -> next;
            curr = curr -> next;
        }
        ListNode* tem = curr -> next -> next;
        curr -> next = tem;
        return new_head.next;
    }

二、栈、队列
1.用队列实现栈
栈的主要操作:push,pop,top,empty()
队列的主要操作有:push,pop,front,empty(),back
主要区别在于栈是先进后出,队列是先进先出。所以主要是push操作时要注意。要让后进来的元素处于队列的头部。使用两个队列来实现。

 queue<int>tem;
    /** Initialize your data structure here. */
    MyStack() {

    }
    
    /** Push element x onto stack. */
    void push(int x) {
        tem.push(x);
        while(!q.empty()){
            tem.push(q.front());
            q.pop();
        }
        while(!tem.empty()){
            q.push(tem.front());
            tem.pop();
        }
    }  
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int tem = q.front();
        q.pop();
        return tem;

    }
    
    /** Get the top element. */
    int top() {
       return q.front();
    } 
    /** Returns whether the stack is empty. */
    bool empty() {
        return q.empty();
    }
    private:
     queue<int>q;

用栈实现队列
使用两个栈来实现

MyQueue() {
    } 
    /** Push element x to the back of queue. */
    stack<int>tem;
    void push(int x) {
        while(!data.empty()){
            tem.push(data.top());
            data.pop();
        }
        data.push(x);
        while(!tem.empty()){
            data.push(tem.top());
            tem.pop();
        }
    } 
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        int x = data.top();
        data.pop();
        return x;
    } 
    /** Get the front element. */
    int peek() {
        return data.top();
    }  
    /** Returns whether the queue is empty. */
    bool empty() {
        return data.empty();
    }
    private:
    stack<int> data;

2.最小栈(155)
利用一个栈专门记录当前状态对应的最小值。

public:
    /** initialize your data structure here. */
    MinStack() {   
    }
    
    void push(int x) {
        
        if(min_data.empty()){
            //min_ = x;
            min_data.push(x);
        }
        else {
            //min_ = min_ > x ? x : min_;
            min_data.push(min(x,min_data.top()));
            }
        data.push(x);
        //min_data.push(min_);
        
    }
    
    void pop() {
        data.pop();
        min_data.pop();
        //min_ = min_data.top();
    }
    
    int top() {
        return data.top();
    }
    
    int getMin() {
        return min_data.top();
    }
private:
stack<int>data;
stack<int>min_data;

3.合法的栈序列
在这里插入图片描述

bool check_is_valid_order(std::queue<int>&order){
         int n = order.size();
         stack<int> s;
         for(int i=1;i<=n;i++){
             s.push(i);
             while(!s.empty()&&s.top()==order.front()){
                 s.pop();
                 order.pop();
             }
         }
         if(!s.empty()){
             return false;
         }
         return true;
     }

4.下一个更大元素(496)
这一类问题统一使用单调栈来解决,单调栈的使得每次入栈之后栈内元素都是保持递增或者递减。单调栈的模板代码:注意从后往前遍历数组

for(int i=len2-1;i>=0;i--){
           while(!s.empty()&&s.top()<=nums2[i]){
               s.pop();//剔除小于当前位的元素
           }
           vc[i] = s.empty() ? -1 : s.top();//vc即为num2数组每一位下一个最大值的数组
           s.push(nums2[i]);
       }

这一题结合map图,先不管num1,先得到num2的每一位对应的下一个最大值,在利用map查找num1中每一位对应的下一位最大值。

  vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        int len1 = nums1.size();
        int len2 = nums2.size();
        vector<int> res(nums1.size(),-1);
        vector<int> vc(nums2.size());
        stack<int>s;
        map<int,int> map1;
       //使用单调栈
       for(int i=len2-1;i>=0;i--){
           while(!s.empty()&&s.top()<=nums2[i]){
               s.pop();
           }
           vc[i] = s.empty() ? -1 : s.top();
           map1[nums2[i]]=vc[i];
           s.push(nums2[i]);
       }
       for(int j=0;j<len1;j++){
           res[j]=map1[nums1[j]];
       }

        return res;
    }

5.下一个更大元素II
使用取余操作%模拟扩展数组的操作。还是使用单调栈完成。
在这里插入图片描述

 vector<int> nextGreaterElements(vector<int>& nums) {
 		int n = nums.size();
        vector<int>res(n);
        stack<int>s;
        for(int i=2*n-1;i>=0;i--){
            
            while(!s.empty()&&s.top()<=nums[i%n]){
                s.pop();
            }
            res[i%n] = s.empty() ? -1 : s.top();
            s.push(nums[i%n]);
       
        }
         return res;
    }

6.用栈操作构建数组(1441)
使用变量index表示数组下标,利用for遍历数数。
注意index不要越界。

 vector<string> buildArray(vector<int>& target, int n) {
        vector<string>res;
        int len = target.size();
        stack<int>s;
        int index = 0;
        for(int i=1;i<=n;i++){
           if(index<len){
               res.push_back("Push");
               s.push(i);
           }
           if(index<len && s.top()!=target[index]){//两个条件还不能互换位置,需要先判断index不越界,否则内存错误。
               res.push_back("Pop");
               s.pop();
           }
           else{
               index++;
           }
        }
        return res;
    }

7.有效括号(20)
使用栈来存储左括号,当遍历到右括号时,从栈的头部取出左括号看是否匹配。注意在此之前要判断栈里是否有元素。最后返回栈是否为空。

bool isValid(string s) {
        stack<char> stack;
        for(int i=0;i<s.length();i++){
            char c = s[i];
            if(c=='('||c=='['||c=='{'){
                stack.push(c);
            }
            else{
                if(stack.empty())return false;
                char b = stack.top();
                stack.pop();
                if(s[i]==')' && b!='(')return false;
                if(s[i]==']' && b!='[')return false;
                if(s[i]=='}' && b!='{')return false;
            }
        }
        return stack.empty();
    }

8.实现循环队列(622)
使用vector来实现循环队列,注意实现循环操作使用取余(%)来实现。
使用head和tail两个指针记录队列的头和尾,在出队的时候实际不用删除容器内的元素,只需要移动相应的头指针就行。

class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        data.resize(k);
        size = k;
        head = tail = -1;
    }
    
    bool enQueue(int value) {
        //case1:队列为空
        if(isEmpty()){
            head = tail = 0;
            data[tail] = value;
            return true;
        }
        //case2:队列满了
        else if(isFull()){
            return false;
        }
        //case3:队列不为空,但还没满
        else{
            tail = (tail+1) % size;
            data[tail] = value;
            return true;
        }

    }
    
    bool deQueue() {
        //case1:队列为空
        if(isEmpty()){
            return false;
        }
        //case2:队列只有一个元素
        else if(tail==head){
            head = tail = -1;
        }
        //case3:队列有多个元素,移动head
        else{
            head = (head+1) % size;
        }
        return true;
    }
    
    int Front() {
        if(isEmpty()){
            return -1;
        }
        else{
            return data[head];
        }

    }
    
    int Rear() {
        if(isEmpty()){
            return -1;
        }
        else{
            return data[tail];
        }
    }
    
    bool isEmpty() {
        if(head==-1){
            return true;
        }
        return false;
    }
    
    bool isFull() {
        if((tail+1)%size==head){
            return true;
        }
        return false;
    }
    private:
        vector<int> data;
        int size;
        int head;
        int tail;
};

9.实现双端循环队列(641)

class MyCircularDeque {
private:
    vector<int> data;
    int size;
    int head;
    int tail;
public:
    /** Initialize your data structure here. Set the size of the deque to be k. */
    MyCircularDeque(int k) {
        data.resize(k);
        size = k;
        head = tail = -1;
    }
    
    /** Adds an item at the front of Deque. Return true if the operation is successful. */
    bool insertFront(int value) {
        //case1:队列为空
        if(isEmpty()){
            head = tail = 0;
            data[head] = value;
            return true;
        }
        //case2:队列为满
        else if(isFull()){
            return false;
        }
        //case3:队列中head==0
        else if(head==0){
            head = size - 1;
            data[head] = value;
            return true;
        }
        //case4:head != 0
        else{
            head -= 1;
            data[head] = value;
            return true;
        }
    }
    
    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    bool insertLast(int value) {
         //case1:队列为空
        if(isEmpty()){
            head = tail = 0;
            data[tail] = value;
            return true;
        }
        //case2:队列为满
        else if(isFull()){
            return false;
        }
        //case3:队列不为满
        else{
            tail = (tail+1) % size;
            data[tail] = value;
            return true;
        }
    }
    
    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    bool deleteFront() {
        //case1:队列为空
        if(isEmpty()){
            return false;
        }
        //case2:队列只有一个元素
        else if(head==tail&&head!=-1){
            head = tail = -1;
            return true;
        }
        //case3:队列有多个元素
        else{
            head = (head+1) % size;
            return true;
        }
    }
    
    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    bool deleteLast() {
        //case1:队列为空
        if(isEmpty()){
            return false;
        }
        //case2:队列只有一个元素
        else if(head==tail&&head!=-1){
            head = tail = -1;
            return true;
        }
        //case3:tail位于0
        else if(tail==0){
            tail = size - 1;
            return true;
        }
        //case4:有多个元素
        else{
            tail = tail - 1;
            return true;
        }
    }
    
    /** Get the front item from the deque. */
    int getFront() {
        if(isEmpty()){
            return -1;
        }
        return data[head];
    }
    
    /** Get the last item from the deque. */
    int getRear() {
        if(isEmpty()){
            return -1;
        }
        return data[tail];

    }
    
    /** Checks whether the circular deque is empty or not. */
    bool isEmpty() {
        if(tail==-1||head==-1){
            return true;
        }
        return false;
    }
    
    /** Checks whether the circular deque is full or not. */
    bool isFull() {
        if((tail+1)%size==head){
            return true;
        }
        return false;
        
    }
};

三、堆
获取最大值:O(1)
删除最大值:O(logn)
添加元素:O(logn)
堆的基本操作:size(),empty(),top(),pop(),push()
1.第k大的数(215)
利用小顶堆维护k个元素的堆
当堆元素少于k个时直接入堆
当元素数量大于K个时,比较输入的元素与堆顶元素,若大于堆顶元素,则弹出堆顶元素,输入元素入堆,进行自动排序。
建立堆使用优先队列

priority_queue<int> pq;//最大值优先队列
priority_queue<int,vector<int>,greater<int> > pq2;//最小值优先队列
std::priority_queue<int,std::vector<int>,std::greater<int>> Q;
int findKthLargest(vector<int>& nums, int k) { 
        std::priority_queue<int,std::vector<int>,std::greater<int>> Q;
        for(int i=0;i<nums.size();i++){
            if(Q.size()<k){
                Q.push(nums[i]);
            }
            else if(nums[i]>Q.top()){
                Q.pop();
                Q.push(nums[i]);
            }
        }
        return Q.top();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值