剑指Offer刷题-一般的题

一般的题

第一个只出现一次的字符

题目描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

思路

方式1.使用map hash
方式2.使用数组哈希count[128]
方式3.使用两个bitset<128>b1,b2;进行标记;
bitset<128>说明其键是128位的。
1)遍历字符串,当前字符为c,若c第一次出现,则b1[c]=0,置b1[c]=1,表示出现过。若c出现第二次,则b1[c]&&!b2[c]==1,即b1[c]=1,b2[c]=0,置b2[c]=1;
2)再次遍历字符串,若b1[c]&&b2[c]==1,说明c出现不止一次;

class Solution {
public:
	// 方式1.
    int mapHash(string str){
        map<char,int>count;
        for(int i=0;i<str.size();i++){
            count[str[i]]++;
        }
        for(int i=0;i<str.size();i++){
            if(count[str[i]]==1){
                return i;
            }
        }
        return -1;
    }
    // 方式2
    int arrayHash(string str){
        int count[128] = {0};
        for(int i=0;i<str.size();i++){
            count[str[i]]++;
        }
        for(int i=0;i<str.size();i++){
            if(count[str[i]]==1){
                return i;
            }
        }
        return -1;
        
    }
    // 方式3
    int bitsetCount(string str){
        bitset<128> b1,b2; // 默认值为0,b1为1 说明出现过 b2为1 说明出现不止1次
        for(int i=0;i<str.size();i++){
            if(!b1[str[i]]&&!b2[str[i]]){
                // b1 b2都为0说明是第一次出现
                b1[str[i]] = 1;
            }else if(b1[str[i]]&&!b2[str[i]]){
                // b1 为1 b2为0 说明出现过了
                b2[str[i]] = 1;
            }
        }
        for(int i=0;i<str.size();i++){
            if(b1[str[i]]&&!b2[str[i]]){
                // 统计完 b1为1 b2为0 说明只出现1次
                return i;
            }
        }
        return -1;
        
    }
    int FirstNotRepeatingChar(string str) {
        /*
            1.方法一、map哈希
            2.方法二、由于全部由字母组成 可以使用128个元素的计数器数组 字符char可以直接作为数组的下标
            3.方法三、128位的bitset【二值集合】bitset<128>
        */
        //return mapHash(str);
        //return arrayHash(str);
        return bitsetCount(str);
    }
};

把数组排成最小的数

题目描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

思路

数组中的数有可能是1位数,也有可能是2位数等多位数。
考虑使用STL的sort排序,设置cmp规则。
将数组前后两个整型数 a ,b 转换成string后拼接,再转换成longlong,按结果更小的排序

class Solution {
public:
    static bool cmp(int a, int b){
        string as = to_string(a);
        string bs = to_string(b);
        // 应该是先拼接 在计算拼接后字符的大小
        return stol( as + bs) < stol(bs + as);
    }
    string PrintMinNumber(vector<int> numbers) {
        sort(numbers.begin(),numbers.end(),cmp);
        string res = "";
        for(int i=0;i<numbers.size();i++){
            res+=to_string(numbers[i]);
        }
        return res;
    }
};

删除链表中重复的节点

题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

思路

链表是排序的
关键要扫描到第一个非重复的节点

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead==NULL)return NULL;
        ListNode* res = new ListNode(pHead->val-1);
        res->next = pHead;
        ListNode* pre = res;
        ListNode* cur = pHead;
        while(cur){
            // 【重点】这里一共有三个关键节点:pre cur 以及 cur->next
            // pre不要动、 让cur 和 cur->next去除重复数字
            if(cur->next!=NULL&&cur->next->val==cur->val){
                cur = cur->next;
                while(cur->next!=NULL&&cur->next->val==cur->val){
                    cur = cur->next;
                }
                cur = cur->next;// cur->next指向了与cur不同的数才出了上述循环
                pre->next = cur;// 这里是pre->next=cur;而不是pre=cur;是假设当前的cur可能是个不重复的数
                // 【巧妙】这里 需要一个if包裹这个while循环
            }else{
                // cur->next==NULL了 或者 
                // cur与cur->next不相等了 且cur不是重复的数
                pre = pre->next; // pre = cur; 也行
                cur = cur->next;
            }
        }
        return res->next;
    }
};

从尾到到尾打印链表

题目描述

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

思路

方式1.借助栈存储节点,然后实现从后向前输出;或是借助vector以及它的reverse方法
方式2.递归方式,参考树的前中后序遍历的递归,先访问后面的节点,在访问当前的节点,再把当前的结果集res返回给上层调用,让上层放入上层访问的节点。

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    // 方式1.借助栈存储节点,然后实现从后向前输出
    vector<int> method1(ListNode* head){
        vector<int>res;
        if(head==NULL)return res;
        
        stack<int> ls;
        ListNode* p = head;
        while(p){
            ls.push(p->val);
            p = p->next;
        }
        while(!ls.empty()){
            res.push_back(ls.top());
            ls.pop();
        }
        return res;
    }
    // 方式2.递归方式,参考树的前中后序遍历的递归,先访问后面的节点,在访问当前的节点
    vector<int> method2(ListNode* head){
        vector<int>res;
        if(head==NULL)return res; // 当访问到末尾空节点的时候,返回一个空的res
        res = method2(head->next);// 最末尾返回一个空的res,
        res.push_back(head->val); // 空的res放入倒数第一个节点,在返回给上一层
        return res;
    }
    vector<int> printListFromTailToHead(ListNode* head) {
        return method2(head);
    }
};

反转链表

题目描述

输入一个链表,反转链表后,输出新链表的表头。

思路

方法一、使用栈维护链表节点
方法二、三个指针

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode *pre = nullptr;
        ListNode *cur = pHead;
        ListNode *nextOne = nullptr; // 初始化为nullptr, 循环里会再次赋值
        while (cur) {
            nextOne = cur->next; // 先找到下一个节点,防止丢失
            cur->next = pre; // 当前节点的next指向前一个节点
            pre = cur;       // 当前节点变成下一轮的前驱节点
            cur = nextOne ;  // 下一个节点变成下一轮的当前节点
        }
        return pre;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值