LeetCode刷题之路,记录刷题成长------随时维护

前言

本博客按照以下顺序逐步的完成,也是一个学习的过程,所以有啥问题可以随时评论。希望和大家一起进步。

栈,队列,堆

栈,队列,堆的数据结构就不多介绍了,只需要知道栈是先进后出,队列是先进先出。在这里就主要介绍一下堆,其实就是一颗二叉树,还是直接上算法及实现方法吧。
1.寻找一个数组中第K大的数
input:[2,3,4,5,6] k=2
output:5

#include<iostream>
#include<queue>
#include<vector>
#include<functional>
using namespace std;
//算法思路
//1.创建一个优先队列大小为K
//2.维护这个优先队列,如果有数字比优先队列的顶端更大则pop顶端,push新的元素,直至结束。
//3.优先队列中保存了前K大的元素,第一个则是第K大的元素
int Kmax(vector<int>&input,int k){
    if(k>input.size())return 0;
    std::priority_queue<int,vector<int>,greater<int>> max_queue;
    int i;
    for(i=0;i<k;++i){
        max_queue.push(input[i]);
      }
    for(i=k;i<input.size();++i){
        if(input[i]>max_queue.top()){
            max_queue.pop();
            max_queue.push(input[i]);
          }
      }
    return max_queue.top();
  }

2.寻找一个数组中的中位数
input:[1,2,3] output:2
input:[1,2,3,4] output:2.5

#include<iostream>
#include<queue>
#include<vector>
#include<functional>
using namespace std;
namespace KMAX {
  int Kmax(vector<int>&input,int k){
    if(k>input.size())return 0;
    std::priority_queue<int,vector<int>,greater<int>> max_queue;
    int i;
    for(i=0;i<k;++i){
        max_queue.push(input[i]);
      }
    for(i=k;i<input.size();++i){
        if(input[i]>max_queue.top()){
            max_queue.pop();
            max_queue.push(input[i]);
          }
      }
    return max_queue.top();
  }
  //获取中位数
  double getMidNum(vector<int>&input){
    if(input.empty())return 0;
    if(input.size()%2!=0){
        return Kmax(input,input.size()/2+1);
      }
    else {
        return (double)(Kmax(input,input.size()/2)+Kmax(input,input.size()/2+1))/2;
      }
  }

结合第一题的思路,其实中位数就是第K/2大的元素,如果k是奇数则直接为k/2+1,例如K=3则就是第二个,也就是3/2+1;同理可得偶数的情况。
面试题 17.14. 最小K个数:设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

#include<iostream>
#include<queue>
#include<vector>
#include<functional>
using namespace std;
 vector<int> smallestK(vector<int>& input, int k) {
      vector<int> output;
      if(k>input.size()){return output;}
      std::priority_queue<int,vector<int>,less<int>> min_queue;
      int i;
      for(i=0;i<k;++i){
          min_queue.push(input[i]);
      }
      if(min_queue.empty())return output;

      for(i=k;i<input.size();++i){
          if(input[i]<min_queue.top()){
              min_queue.pop();
              min_queue.push(input[i]);
          }
      }
    while(!min_queue.empty()){
        output.push_back(min_queue.top());
        min_queue.pop();
    }
  return output;
  }

给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。

初始化 A 和 B 的元素数量分别为 m 和 n。

示例:

输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3

输出: [1,2,2,3,5,6]

//逆向双指针
 void merge(vector<int>& A, int m, vector<int>& B, int n) {
    int pa = m - 1, pb = n - 1;
    int tail = m + n - 1;
    int cur;
    while (pa >= 0 || pb >= 0) {
        if (pa == -1)
            cur = B[pb--];
        else if (pb == -1)
            cur = A[pa--];
        else if (A[pa] > B[pb])
            cur = A[pa--];
        else
            cur = B[pb--];
        A[tail--] = cur;
    }
    
  
      }

链表

链表的概念就先放着,直接上链表的头插法,尾插法。
链表记住:让新来的节点有所指向,前驱节点指向新来的节点
头插法:

//头插法
void head_push(ListNode*head,int val){
  ListNode* node = new ListNode(val);
  node->next = head->next;
  head->next = node;
}

尾插法

//(尾插法)
void push_back(ListNode*head,int val){
  ListNode* node = new ListNode(val);
  ListNode* pre = head;
  while (pre->next) {
      pre=pre->next;
    }
  pre->next = node;

}

链表的反转
反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

//链表反转
ListNode* revolveList(ListNode*head){
  ListNode* pre =NULL;
  while(head){
      ListNode *temp = head->next;
      head->next = pre; //新来的指针有所指向
      pre = head;
      head= temp;
    }
   return pre;


}

反转从位置 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) {
        int change_len = n-m+1;
        ListNode* pre_head = NULL;
        ListNode* result =head;
        while(head&&--m){
            pre_head = head;
            head = head->next;
        }
        //记录起始地点,反转后即为末尾地点
        ListNode*modify_lise_tail = head;
        ListNode *new_head = NULL;
        while(head&&change_len){
            //头插法
            ListNode* next = head->next;
            head->next = new_head;
            new_head = head;
            head = next;
            --change_len;
        }
        modify_lise_tail->next = head;
        if(pre_head){
            pre_head->next = new_head;
        }else{
            result = new_head;

        }
        return result;
    }

//检查链表是否有环
//快慢指针法

int num =0;
    if(!head)return 0;
    ListNode * fast=head;
    ListNode * slow=head;
    while(fast->next&&slow->next){
        fast = fast->next;
        slow = slow->next;
        if(fast->next==NULL){
            return 0;
          }
        fast = fast->next;
        if(fast == slow){
            return 1;
          }
        num++;
        if(num>10000000){
            break;
          }
      }
    return 0;

//给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
//算法思路
1.确定一个快慢指针,fast = 2slow
2.当第一次相遇时,假设从头节点到环起始点的距离为a,从环的起始点到相遇点的距离为b,从相遇点到环起点的距离为c,则第一次相遇时,慢指针走过的路程为a+b,快指针走过的路程为a+b+c+b。
3,为此满足2(a+b) = a+2b+c,由此得到a = c。
4,所以只需要让快指针在相遇的位置,一次向前移动一步,让慢指针指向头节点,一次移动一步相遇时,即为环的起点。

 ListNode *detectCycle(ListNode *head) {
        int num =0;
        if(!head)return NULL;
        ListNode * fast=head;
        ListNode * slow=head;
        while(fast->next&&slow->next){
            fast = fast->next;
            slow = slow->next;
            if(fast->next==NULL){
                return NULL;
            }
            fast = fast->next;
            if(fast == slow){
                slow = head;
                while(!(slow == fast)){
                    slow = slow->next;
                    fast = fast->next;
                }
                return slow;
            }
            num++;
            if(num>10000000){
                break;
            }
        }
        return NULL;
    }

贪心

//分糖果
//假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
//对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,
//都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。
//你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
//leetcode 455

int findContentChildren(std::vector<int>&g,vector<int>&s){
    int child =0;
    sort(g.begin(),g.end());
    sort(s.begin(),s.end());
    for(int i=0;i<s.size();++i){
        if(child<g.size()&&s[i]>=g[child]){
            child++;
        }
    }
    return child;

}

//摇摆序列
//leetcode 376 wiggle subsequence
//如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列

int wiggleMaxLength(vector<int>& nums) {
    if(nums.empty())return 0;
    int num =1;
    int begin = nums[0];
    const int UP=1;
    const int DOWN = 2;
    const int BEGIN =0;
    int STATE = 0;

    for(int i=1;i<nums.size();++i){
        if(begin>nums[i]){
            switch (STATE)
            {
            case UP:
                /* code */
                begin =nums[i];
                num++;
                STATE = DOWN;
                break;
            case DOWN:
                begin = nums[i];
                break;
            case BEGIN:
                begin = nums[i];
                STATE = DOWN;
                num++;
                break;
            
            default:
                break;
            }
            
        }else if(begin<nums[i]){
            switch (STATE)
            {
            case UP:
                /* code */
                begin = nums[i];
                break;
            case DOWN:
                begin = nums[i];
                num++;
                STATE = UP;
                break;
            case BEGIN:
                begin = nums[i];
                STATE = UP;
                num++;
                break;
            
            default:
                break;
            }
           
        }else{
            continue;
        }

    }
    return num;
}

移除k个数字使得数字最小
leetcode 402

string removeKdigits(string num, int k) {
    vector<char> s;
    string result = "";
    for(int i=0;i<num.length();++i){
        while (s.size()!=0&&s.back()>num[i]&&k>0)
        {
            /* code */
            s.pop_back();
            k--;
        }
        if(num[i]!='0'||s.size()!=0){
            s.push_back(num[i]);
        }
        
    } 
    while (s.size()!=0&&k>0)
    {
        /* code */
        s.pop_back();
        k--;
    }
    for(int i=0;i<s.size();++i){
        result.append(1,s[i]);
    }
    result = result==""?"0":result;
    
   
    return result;
}

跳跃游戏
leetcode 55 跳跃游戏

bool canJump(vector<int>& nums) {
     vector<int> jumpPosition;
     for(int i=0;i<nums.size();++i){
         jumpPosition.push_back(i+nums[i]);
     }
     int jump =0;
     int max_index = jumpPosition[0];
     while(jump<jumpPosition.size()&&jump<max_index){
         if(max_index<jumpPosition[jump]){
             max_index = jumpPosition[jump];

         }
         jump++;
     }
     return jump==jumpPosition.size();

}

跳跃游戏二
leetcode 45 跳跃游戏二

int jump(vector<int> & nums){
    if(nums.size()<2){
        return 0;
    }
    int jumpMin =1;
    int currentMaxIndex = nums[0];
    int preMaxIndex = nums[0];
    for(int i=0;i<nums.size();++i){
        if(i>currentMaxIndex){
            jumpMin++;
            currentMaxIndex = preMaxIndex;
        }
        preMaxIndex = preMaxIndex>nums[i]+i?preMaxIndex:nums[i]+i;
    }
   return jumpMin;




}

射击气球
leetcode 452 用最少数量的箭引爆气球

bool cmp(vector<int>& a,vector<int>& b){
    return a[0]<b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
    if(points.empty()){
        return 0;
    }
    sort(points.begin(),points.end(),cmp);
    int shootNum =1;
    int shootBegin = points[0][0];
    int shootEnd = points[0][1];
    for(int i=1;i<points.size();++i){
        if(points[i][0]<= shootEnd){
            shootBegin = points[i][0];
            if(shootEnd>points[i][1]){
                shootEnd = points[i][1];
            }
        }else{
            shootNum++;
            //新的区间
            shootBegin = points[i][0];
            shootEnd = points[i][1];
        }
    }
    return shootNum;
}

//用户分组
//leetcode 1282 用户分组

vector<vector<int>> groupThePeople(vector<int>& groupSizes) {
    //第一步 先把相同的用户组都存到一起pair<用户组的大小,用户组的成员(vector)>
    //对这个pair中的成员进行分类
    if(groupSizes.empty()){
        return vector<vector<int>>();
    }
    vector<vector<int>> result;
    unordered_map<int,vector<int>> ump;
    for(int i = 0;i<groupSizes.size();++i){
        if(ump.find(groupSizes[i])!=ump.end()){
            ump[groupSizes[i]].push_back(i);
        }else
        {
            vector<int> newV {i};
            ump.insert(pair<int,vector<int>>(groupSizes[i],newV));
        }
        
    }
    for(auto k:ump){
        while(!k.second.empty()){
            vector<int> temp;

            for(int j=0;j<k.first;++j){
                temp.push_back(k.second.back());
                k.second.pop_back();
            }
            result.push_back(temp);
        }
    }
    return result;
}

递归、回溯、分治

寻找所有子集
给定一个集合,输出该集合的所有子集

#include<iostream>
#include<vector>

using namespace std;
void subsetR(int i,vector<int>& num,vector<int>&temp,vector<vector<int>>&outPut){
    if(i>=num.size()){
        return;
    }
    //一个元素有两种选择
    temp.push_back(num[i]);
    outPut.push_back(temp);
    subsetR(i+1,num,temp,outPut);
    temp.pop_back(); //回溯
    subsetR(i+1,num,temp,outPut);
    

}
vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> temp;
        vector<vector<int>> results;
        subsetR(0,nums,temp,results);
        results.push_back(vector<int>());
        return results;
}

非递归版

vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        int all_set = 1<<nums.size();//一共有多少种取法
        for(int i=0;i<all_set;++i){
            vector<int> temp;
            for(int j=0;j<nums.size();++j){
                if(i&(1<<j)){
                    //j表示从第0个到nums.size()-1的位置,例如第
                    temp.push_back(nums[j]);
                }
            }
            result.push_back(temp);
        }
        return result;
}

至于为什么是这样的,leetcode上有非常详细的过程。

二叉树与图

二分查找与二叉排序树

哈希表与字符串

搜索

动态规划

插叙(就是随便写写)

//序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
//输入:target = 9
//输出:[[2,3,4],[4,5]]
思路:创建一个链表,如果链表内的元素比目标小,就从后面push,如果大,则从前pop直至链表内的元素比目标小或者相等,如果相等则直接把链表元素复制出来。整个链表的循环一共循环target/2+1次。最后直到链表为空,返回结果。

vector<vector<int>> findContinuousSequence(int target) {
      vector<vector<int>> output;
      if(target<2){
          return output;
        }
      std::list<int> li;
      li.push_back(1);

      int sum =1;
      int i=2;
      //终止条件
      while(!li.empty()){
      //往链表添加元素
          for(;i<target/2+2;++i){
              
              if(sum == target){
                  vector<int> temp;
                  for(auto n:li){
                      temp.push_back(n);
                    }
                  output.push_back(temp);
                }
              li.push_back(i);
              sum+=i;

              while(sum>target){
                  sum-=li.front();
                  li.pop_front();
                }
            }
          if(sum == target){
              vector<int> temp;
              for(auto n:li){
                  temp.push_back(n);
                }
              output.push_back(temp);
            }
          sum-=li.front();
          li.pop_front();

        }
      return output;
   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值