算法题c++刷题记录之数据结构部分


1.两个栈实现一个队列
题目描述:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
分析:栈是先进后出,队列是先进先出
因此 push的话直接在stack1读入就好了
pop的话,需要从stack1压入stack2,再由stack2弹出
注意:若stack2是空的,需要从stack1向其压入数据
若stack2不空,则须stack2先弹出,空了之后,stack1压入
否则会影响元素弹出的位置

1# 运行时间:25ms
# 占用内存:5724k
# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
        
    def push(self, node):
        # write code here
        self.stack1.append(node)
        
    def pop(self):
        # return xx
        if len(self.stack1) == 0 and len(self.stack2) == 0:
            return 
        if len(self.stack2) == 0:
            while len(self.stack1) != 0:
                self.stack2.append(self.stack1.pop())
        return self.stack2.pop()``
 
  法2 
#运行时间:26ms 占用内存:5736k
class Solution:
    def __init__(self):
        self.stack1=[]
        self.stack2=[]
    def push(self, node):
        # write code here
        self.stack1.append(node)
    def pop(self):
        # return xx
        if self.stack2:
            return self.stack2.pop()
        else:
            while(self.stack1):
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()
    

2.旋转数组的最小值
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
借鉴大佬思路:二分法的变形,注意,若出现110111这样的,mid=high,只能high=high-1,挨个查找

#运行时间:665ms 占用内存:5852k
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        low,high=0,len(rotateArray)-1
        while low<high:
            mid=(low+high)//2
            if rotateArray[high]<rotateArray[mid]:
                low=mid+1
            elif rotateArray[high]>rotateArray[mid]:
                high=mid
            else:
                high=high-1
        return  rotateArray[low]

c++版本

class Solution {
   
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
   
        if (rotateArray.empty()) return 0;
        int left = 0, right = rotateArray.size() - 1;
        while (left < right) {
   
            //确认子数组是否是类似1,1,2,4,5,..,7的非递减数组
            if (rotateArray[left] < rotateArray[right]) return rotateArray[left];
             
            int mid = left + (right - left) / 2;
            //如果左半数组为有序数组
            if (rotateArray[left] < rotateArray[mid])
                left = mid + 1;
            //如果右半数组为有序数组
            else if (rotateArray[mid] < rotateArray[right])
                right = mid;
            //否则,rotateArray[left] == rotateArray[mid] == rotateArray[right]
            else {
   
                ++left;
            }
        }
        return rotateArray[left];
    }
};

3.二维数组中的查找
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:矩阵有序,所以从左下角进行查找,若>目标值,向上一行,<目标值,向右一行,时间复杂度O(M+N)
注:python二维数组行列长度获取 输出行列array.shape
行:array.shape[0],列:array.shape[1]

class Solution:
    #array 二维列表
    def Find(self, target, array):
        row=len(array)
        col=len(array[0])
        i,j=row-1,0
        while i>=0 and j<col:
            if array[i][j]==target:
                return True
            elif array[i][j]>target:
                i=i-1
            else:
                j=j+1
        return False
        
    
        

4.数组中的重复数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
大佬的思路:将数组保存为哈希表,然后再遍历依次即可。哈希表的查找时间是O(1),但需要额外的存储空间。不知道为什么测试没有通过

class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        if numbers == None:
            return None
        count.dict={
   }
        for num im numbers:
            count_dict[num]=count_dict.get(num,0)+1
        for num in numbers:
            if count_dict[num]>1:
                duplication[0]=num
                return True
        return False

大佬做法2:直接利用sort()函数进行排序,不知道算不算作弊操作

class Solution:
    #这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        numbers.sort()  #sort对数组进行排序
        for i in range(len(numbers)-1):
            if numbers[i] == numbers[i+1]:
                duplication[0]=numbers[i]
                return True
        return False

5.数在排序数组中出现的速度
思路1:遍历

class Solution:
    def GetNumberOfK(self, data, k):
        count=0
        for num in data:
            if num==k:
                count +=1
        return count 

思路2:利用二分法,找到k的最左边和最右边对应的位置,相减
代码待补充

6.替换空格
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
大佬思路:先遍得到空格数count
再从后往前,遇到非空格往后挪count2个位置
遇到空格,该位置后移2
(count-1)个位置,其它依次

class Solution {
   
public:
    void replaceSpace(char *str,int length) {
   
        int count=0;
        for(int i=0;i<length;i++){
   
            if(str[i]==' ')
                count++;
        }
        for(int i=length-1;i>=0;i--){
   
            if(str[i]!=' '){
   
                str[i+2*count]=str[i];
            }
            else{
   
                count--;
                str[i+2*count]='%';
                str[i+2*count+1]='2';
                str[i+2*count+2]='0';
            }
        }
    }
};

7.二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
补码的表示方法是:正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

大佬思路:
一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。(负数用补码表示,所以是一样的道理)

class Solution {
   
public:
     int  NumberOf1(int n) {
   
         int count=0;
         if(n==0){
   return 0;}
         while(n!=0){
   
             count++;
             n=n & n-1;
                 
         }
          return count;   
         
     }
};

8.写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号
两个数异或:相当于每一位相加,而不考虑进位;
两个数相与,并左移一位:相当于求得进位;
将上述两步的结果相加

13+11 = ?;
13 的二进制 1 1 0 1 -----a 13
11 的二进制 1 0 1 1 -----b 11

(a&b) <<1 -> 1 0 0 1 0 -----d 18
a^b -> 0 1 1 0 -----e 6

(d&e) <<1 -> 0 0 1 0 0 ------f 4
d^e -> 1 0 1 0 0 -----g 20

(f&g) <<1 -> 0 1 0 0 0 ------h 8
f^g -> 1 0 0 0 0 ------i 16

(h&i) <<1 -> 0 0 0 0 0 ------h 0 ---- --------退出循环
h^i -> 1 1 0 0 0 ------i 24

#递归
#4ms
class Solution {
   
public:
    int Add(int num1, int num2)
    {
   
        if(num2==0)
            return num1;
        return Add(num1^num2, (num1&num2)<<1);
    }
};
2:迭代
//3ms
class Solution {
   
public:
    int Add(int num1, int num2)
    {
   
        while(num2!=0){
   
            int sum1=num1^num2;
            int sum2=(num1&num2)<<1;
            num1=sum1;
            num2=sum2;
                
        }
        return num1;
    }

9.设计一个函数, 可以将任意十进制的数, 转换成任意2到9的进制表示的形式
需要转换的数字x(0<=x<=1e18) 转换使用的进制k(2<=k<=9)

#include <bits/stdc++.h>
using namespace std;
 
int main(){
   
    long x;
    int k;
    cin>>x>>k;
    stack<int> s;
    if(x==0)
        cout<<0<<endl;
    else{
   
        while(x){
   
            s.push(x%k);
            x /= k;
        }
        while(!s.empty()){
   
            cout<<s.top();
            s.pop();
        }
        cout<<endl;
    }
    return 0;
}

10 跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路:第n阶可以从n-2阶跳,也可以用n-1阶跳。
所以:f(n)=f(n-1)+f(n-1) 斐波那契数列

#注意初始条件
#迭代
class Solution {
   
public:
    int jumpFloor(int number) {
   
        
        if(number==0){
   return 0;}
        if(number==1){
   return 1;}
        if(number==2){
   return 2;}
        int prenum=2;
        int preprenum=1;
        int result=0;
        for(int i=3;i<=number;i++){
   
            result=prenum+preprenum;
            preprenum=prenum;
            prenum=result;
        }
        return result;    
        
    }

10.变态版跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
分析:大佬的思路:
每个台阶可以看作一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子, 必须存在,其他 (n-1) 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法,可以直接得到结果。

class Solution {
   
public:
    int jumpFloorII(int number) {
   
        return 1<<(number-1);  //用位移运算做2^(n-1),速度更快

    }
};

因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级
跳1级,剩下n-1级,则剩下跳法是f(n-1)
跳2级,剩下n-2级,则剩下跳法是f(n-2)
所以f(n)=f(n-1)+f(n-2)+…+f(1)
因为f(n-1)=f(n-2)+f(n-3)+…+f(1)
所以f(n)=2*f(n-1)
迭代

链表

11.寻找链表的倒数第k个节点
思路:定义两个指针,快的先跑k步然后快慢一起跑,快的到空的时候,慢指针就在倒数第k个节点
时间复杂度O(n),空间复杂度O(1)

class Solution {
   
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
   
        if(pListHead == nullptr || k < 1){
   
            return nullptr;
        }
        ListNode *pre = pListHead;
        ListNode *last = pListHead;
        while(k > 0){
   
            if(last == nullptr){
   
                return nullptr;
            }
            last = last->next;
            k--;
        }
        while(last != nullptr){
   
            last = last->next;
            pre = pre->next;
        }
        return pre;
    }
};

删除倒数第k个节点

class Solution {
   
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
   
        if(head==nullptr ||  n<1){
   
            return nullptr;
        }
        ListNode* pre=head;
        ListNode* last=head;
        while(n>0){
   
            if(last==nullptr){
   
                return nullptr;
            }
            last=last->next;
            n--;
        }
        //当n=链表的长度时,直接删除头节点,返回head->next
        if(!last){
   
            return head -> next;    
        }
        while(last->next!=nullptr){
    //这样找出来的pre为第k+1个节点
            last=last->next;
            pre=pre->next;
        }
        pre->next=pre->next->next;
        return head;


    }
};

12,合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

//递归法
class Solution {
   
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
   
        ListNode* node=NULL;
        if(pHead1==NULL){
   return node=pHead2;}
        if(pHead2==NULL){
   return node=pHead1;}
        if(pHead1->val<pHead2->val){
   
            node=pHead1;
            node->next=Merge(pHead1->next,pHead2);
        }else
            {
   
            node=pHead2;
            node->next=Merge(pHead1,pHead2->next);
        }
        return node;
         
    }
     
};
//非递归
class Solution {
   
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
   
       
        ListNode *p = new ListNode(0);
        ListNode *head = p;
        while (pHead1 && pHead2)
        {
   
            if (pHead1->val < pHead2->val)
            {
   
                head->next = pHead1; 
                pHead1 = pHead1->next;
            }
            else
            {
   
                head->next= pHead2;
                pHead2 = pHead2->next;
            }
            head = head->next;
        }
        if (pHead1)
            head->next = pHead1;
        if (pHead2)
            head->next = pHead2;
        return p->next; 
    }
};

13.链表反转

//头插法
class Solution {
   
public:
    ListNode* ReverseList(ListNode* pHead) {
   
        ListNode* root=new ListNode(0);
        ListNode* next=NULL;
        while(pHead!=NULL){
   
            next=pHead->next;
            pHead->next=root->next;  //将根节点的后继变成pHead的后继
            root->next=pHead;//将pHead的值给根结点的后继
            pHead=next;
                
        }
        return root->next;

    }
//依次反转法(三个指针)
class Solution {
   
public:
    ListNode* reverseList(ListNode* head) {
   
        ListNode* pre=NULL;
        ListNode* curr=head;
        while(curr!=NULL){
   
            ListNode* next=curr->next;//保存后节点的值
            curr->next=pre;
            pre=curr; //前指针后移一步
            curr=next;//当前指针后移一步
        }
        return pre;


    }
};
//递归的方法其实是非常巧的,它利用递归走到链表的末端,
//然后再更新每一个node的next 值 ,实现链表的反转。
//而newhead 的值没有发生改变,为该链表的最后一个结点
//所以,反转后,我们可以得到新链表的head。

class Solution {
   
public:
    ListNode* ReverseList(ListNode* pHead) {
   
        //如果链表为空或者链表中只有一个元素
        if(pHead==NULL||pHead->next==NULL) return pHead;
         
        //先反转后面的链表,走到链表的末端结点
        ListNode* pReverseNode=ReverseList(pHead->next);
         
        //再将当前节点设置为后面节点的后续节点
        pHead->next->next=pHead;
        pHead->next=NULL;
         
        return pReverseNode;
         
    }
};

14.从尾到头打印链表

//利用栈先进后出的特性
class Solution {
   
public:
    vector<int> printListFromTailToHead(ListNode* head) {
   
        vector<int> value;
        stack<int> stk;
        ListNode* p=NULL;
        p=head;
        while(p!=NULL){
   
            stk.push(p->val);
            p=p->next;
        }
        while(!stk.empty()){
   
            value.push_back(stk.top());
            stk.pop();
        }
        return value;
        
            
        
    }
};
//递归
class Solution {
   
public:
    vector<int> value;
    vector<int> printListFromTailToHead(ListNode* head) {
   
        
        if(head!=NULL){
   
            if(head->next!=NULL){
   
                value=printListFromTailToHead(head->next);
            }
            value.push_back(head->val);
        }
        return value;
    }
};

15.找两个链表的第一个公共节点
思路:若有公共节点,公共节点长度为c
则:A=a+c;B=b+c;
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点
A: a1 → a2

c1 → c2 → c3

B: b1 → b2 → b3

class Solution {
   
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
   
        ListNode* l1=pHead1;
        ListNode* l2=pHead2;
        while(l1!=l2){
     //当l1和l2相等时,证明到了第一个公共节点,退出循环,返回当前的l1或l2
            l1=(l1==NULL)? pHead2:l1->next;
            l2=(l2==NULL)? pHead1:l2->next;
        }
        return l1;
        

16 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次
思路1:递归套路解决链表问题:
找终止条件:当head指向链表只剩一个元素的时候,自然是不可能重复的,因此return
想想应该返回什么值:应该返回的自然是已经去重的链表的头节点
每一步要做什么:宏观上考虑,此时head.next已经指向一个去重的链表了,而根据第二步,我应该返回一个去重的链表的头节点。因此这一步应该做的是判断当前的head和head.next是否相等,如果相等则说明重了,返回head.next,否则返回head

class Solution {
   
public:
    ListNode* deleteDuplicates(ListNode* head) {
   
        if(head==
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值