《剑指offer》-数据结构篇-哈希表/数组/矩阵/字符串

题目

  1. 第一个只出现一次的字符

  2. 数组中的重复的数字

  3. 字符串流中第一个不重复的字符

  4. 数组中只出现一次的数字

  5. 调整数组顺序使奇数位于偶数前面

  6. 数组中出现次数超过一半的数字

  7. 把数组排成最小的数

  8. 顺时针打印矩阵

  9. 把字符串转换为整数

  10. 表示数值的字符串

  11. 左旋转字符串(矩阵翻转)

  12. 替换空格

  13. 正则表达式匹配

代码实现

第一个只出现一次的字符

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

思路:遍历字符串的所有字符,将字符和对应的出现次数构成"键值对"。再次遍历,找到第一个值为1的键,即为所求的字符。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        #采用字典的方式
        map = {}
        for i in range(len(s)):
            #字典的.get(key,value)方法对字典中没有的key赋一个value值,因而这里需要指定value值;
            #如果不指定,使用.get(key)得到字典中已有的的对应于key的value值
            map[s[i]] = map.get(s[i], 0) + 1
        for i in range(len(s)):
            if map[s[i]] == 1:
                return i
        return -1

数组中的重复的数字

题目描述:

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

思路:和第1题类似,这里使用列表存储数字出现的次数。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        map = [0] * 1000
        for n in numbers:
            if map[n] == 1:
                duplication[0] = n
                return True
            else:
                map[n] = 1
        return False

字符串流中第一个不重复的字符

题目描述:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符"google"时,第一个只出现一次的字符是"l"。如果当前字符流没有存在出现一次的字符,返回#字符。

思路:用列表来实现栈,栈中只保留连续字符中不重复的字符。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # 返回对应char
    def __init__(self):
        self.data = []
    def FirstAppearingOnce(self):
        # write code here
        return self.data[0] if self.data else '#'
    def Insert(self, char):
        # write code here
        n = len(self.data)
        for i in range(n-1, -1, -1):
            if self.data[i] == char:
                self.data.pop(i)
                return
        self.data.append(char)

数组中只出现一次的数字

题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

思路:和第1题类似。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        map = {}
        res = []
        for n in array:
            map[n] = map.get(n, 0) + 1
        for n in array:
            if map[n] == 1:
                res.append(n)
        return res

调整数组顺序使奇数位于偶数前面

题目描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

思路:统计得到数组的长度,然后将奇数依次放在前半部分,偶数也类似。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        odd_cnt = 0
        res = [0] * len(array)
        # 统计个数
        for n in array:  
            if n % 2 != 0:
                odd_cnt += 1
        # 填坑
        odd_i = 0
        even_i = odd_cnt
        for i in range(len(array)):
            if array[i] % 2 != 0:  
                res[odd_i] = array[i]
                odd_i += 1
            else:
                res[even_i] = array[i]
                even_i += 1
        return res

数组中出现次数超过一半的数字

题目描述:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路:找到出现次数最多的那个数字,然后判断其出现的次数是否超过数组长度的一半。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        res = None
        cnt = 0
        # 留下数组中出现次数最高的数
        for i in numbers:  
            if not res:
                res = i
                cnt = 1
            else:
                if i == res:
                    cnt += 1
                else:
                    cnt -= 1
                    if cnt == 0:
                        res = None
        # 判断次数是否大于一半
        cnt = 0 
        for i in numbers:
            if i == res:
                cnt += 1
        if cnt > len(numbers) / 2:
            return res
        else:
            return 0

把数组排成最小的数

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

思路:首先,找到数组中元素的最大长度n。然后,将数组中的其它元素按照元素+元素最后一位(补至第n-1位)+元素在数组中的序号的形式改写。最后,对补齐后的结果进行排序,找到最小的元素,并找到其所对应的原数组的元素值,把它作为所能组成的数字的最高几位,依次按照这种方式将数组中的数字排列在一起构成最小的数字。本人觉得本题较难!

python 复制代码
# -*- coding:utf-8 -*-
#计算数组中的数字的长度
def number_len(n):
    res = 0
    while n:
        n //= 10
        res += 1
    return res
 
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        #考虑特殊情况
        if not numbers:
            return ""
        if len(numbers) == 1: return numbers[0]
        #补齐数字的长度
        #例如:{3, 32, 321}补齐成{3331, 3222, 3213},{1, 11, 111}补齐成{1111, 1112, 1113}
        max_len = number_len(max(numbers))
        map = {}
        for i in range(len(numbers)):
            n = numbers[i]
            pad = n % 10
            n_len = number_len(n)
            for j in range(max_len-n_len):
                n = n*10+pad
            map[n*10+n_len] = i
        #对补齐后的结果进行排序,找到最小的元素,然后根据原每个数组中的数字的长度乘以10的指数次方
        keys = sorted(map.keys())
        res = numbers[map[keys[0]]]
        for i in range(1, len(keys)):
            n = numbers[map[keys[i]]]
            res = res * 10 ** number_len(n) + n
        return res

顺时针打印矩阵

题目描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10。

思路:依次打印,注意停止条件。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
    # write code here
        walked = [[False] * (len(matrix[0])+1) for _ in range(len(matrix)+1)]
        for j in range(len(walked[-1])):
            walked[-1][j] = True
        for i in range(len(walked)):
            walked[i][-1] = True
        len_row = len(matrix) - 1
        len_col = len(matrix[0]) - 1
        res = []
        i = 0
        j = 0
        direction = 0  # 0向右,1向下,2向左,3向上
 
        while not walked[i][j]:
            res.append(matrix[i][j])
            walked[i][j] = True
            if direction == 0: # right
                if j < len_col and not walked[i][j+1]:
                    j += 1
                else:
                    direction = 1
                    i += 1
            elif direction == 1: # down
                if i < len_row and not walked[i+1][j]:
                    i += 1
                else:
                    direction = 2
                    j -= 1
            elif direction == 2:  # left
                if j > 0 and not walked[i][j-1]:
                    j -= 1
                else:
                    direction = 3
                    i -= 1
            elif direction == 3:  # up
                if i > 0 and not walked[i-1][j]:
                    i -= 1
                else:
                    direction = 0
                    j += 1
        return res

把字符串转换为整数

题目描述:将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。输入一个字符串,包括数字字母符号,可以为空。如果是合法的数值表达则返回该数字,否则返回0。 思路:判断首字符是"+"/"-"。然后,判断其它字符的ASCII/Unicode码是否在"0"和"9"所对应的数值的大小范围之内,如果是的话说明是数字,进行转变后加到整数的对应位上。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    def StrToInt(self, s):
        # write code here
        res = 0
        flag = 1
        for i in range(len(s)):
            #判断数值是正是负
            if i == 0 and s[i] == '+':
                continue
            elif i == 0 and s[i] == '-':
                flag = -1
                continue
            #将每个字符转换为i"二进制"的表示结果
            #返回对应的ASCII数值,或者Unicode数值,如果所给的Unicode
            #字符超出了你的Python定义范围,则会引发一个TypeError的异常。
            n = ord(s[i]) - ord('0')
            if n>=0 and n<=9:
                res = 10 * res + n
            else:
                return False
        return res * flag

表示数值的字符串

题目描述:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

思考:考虑不表示数值的字符串的情况,然后逐个进行判断。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        sign, point, hasE = False, False, False
        for i in range(len(s)):
            if s[i].lower() == 'e':
                #不出现两个'e'
                if hasE: return False
                #不能使得字符串的最后一个字符是'e'
                if i == len(s)-1: return False
                hasE = True
            elif s[i] == '+' or s[i] == '-':
                #不能同时出现'e'和'+'或'-'
                if sign and s[i-1].lower() != 'e': return False
                if not sign and i > 0 and s[i-1].lower() != 'e': return False
                sign = True
            elif s[i] == '.':
                #不出现连续的'.'
                #不能既有'.',又有'e'
                if hasE or point: return False
                point = True
            #不出现0~9之外的其它字符
            elif ord(s[i]) < ord('0') or ord(s[i]) > ord('9'):
                return False
        return True

左旋转字符串(矩阵翻转)

题目描述:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S="abcXYZdef",要求输出循环左移3位后的结果,即"XYZdefabc"。是不是很简单?OK,搞定它!

思路:对于一个字符串XY,在X与Y的交界处循环左移,使得结果为YX。可以先得到YTXT(T表示翻转,XT表示对X进行翻转),再得到YTX,最后得到YX。

python 复制代码
# -*- coding:utf-8 -*-
def reverse(str, s, e):
    e -= 1
    while s < e:
        str[s], str[e] = str[e], str[s]
        s += 1
        e -= 1
        
class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if len(s) == 0 or n == 0: return s
        s = list(s)
        #对于一个字符串XY,在X与Y的交界处循环左移,使得结果为YX
        #可以先得到YTXT(T表示翻转,XT表示对X进行翻转),再得到YTX,最后得到YX
        reverse(s, 0, n)
        reverse(s, n, len(s))
        reverse(s, 0, len(s))
        return ''.join(s)

替换空格

题目描述:请实现一个函数,将一个字符串中的每个空格替换成"%20"。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

思考:找到""并进行替换,利用列表的特性。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        s = list(s)
        count=len(s)
        for i in range(0,count):
            if s[i]==' ':
                s[i]='%20'
        return ''.join(s)

正则表达式匹配

题目描述:请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。

思路:分不同的情况进行罗列。本人觉得题目较难。

python 复制代码
# -*- coding:utf-8 -*-
class Solution:
    # s, pattern都是字符串
    def match(self, s, pattern):
        # 如果s与pattern都为空,则True
        if len(s) == 0 and len(pattern) == 0:
            return True
        # 如果s不为空,而pattern为空,则False
        elif len(s) != 0 and len(pattern) == 0:
            return False
        # 如果s为空,而pattern不为空,则需要判断
        elif len(s) == 0 and len(pattern) != 0:
            # pattern中的第二个字符为*,则pattern后移两位继续比较
            if len(pattern) > 1 and pattern[1] == '*':
                return self.match(s, pattern[2:])
            else:
                return False
        # s与pattern都不为空的情况
        else:
            # pattern的第二个字符为*的情况
            if len(pattern) > 1 and pattern[1] == '*':
                # s与pattern的第一个元素不同,则s不变,pattern后移两位,相当于pattern前两位当成空
                if s[0] != pattern[0] and pattern[0] != '.':
                    return self.match(s, pattern[2:])
                else:
                    # 如果s[0]与pattern[0]相同,且pattern[1]为*,这个时候有三种情况
                    # pattern后移2个,s不变;相当于把pattern前两位当成空,匹配后面的
                    # pattern后移2个,s后移1个;相当于pattern前两位与s[0]匹配
                    # pattern不变,s后移1个;相当于pattern前两位,与s中的多位进行匹配,因为*可以匹配多位
                    return self.match(s, pattern[2:]) or self.match(s[1:], pattern[2:]) or self.match(s[1:], pattern)
            # pattern第二个字符不为*的情况
            else:
                if s[0] == pattern[0] or pattern[0] == '.':
                    return self.match(s[1:], pattern[1:])
                else:
                    return False

结尾

亲爱的读者朋友:感谢您在繁忙中驻足阅读本期内容!您的到来是对我们最大的支持❤️

正如古语所言:"当局者迷,旁观者清"。您独到的见解与客观评价,恰似一盏明灯💡,能帮助我们照亮内容盲区,让未来的创作更加贴近您的需求。

若此文给您带来启发或收获,不妨通过以下方式为彼此搭建一座桥梁: ✨ 点击右上角【点赞】图标,让好内容被更多人看见 ✨ 滑动屏幕【收藏】本篇,便于随时查阅回味 ✨ 在评论区留下您的真知灼见,让我们共同碰撞思维的火花

我始终秉持匠心精神,以键盘为犁铧深耕知识沃土💻,用每一次敲击传递专业价值,不断优化内容呈现形式,力求为您打造沉浸式的阅读盛宴📚。

有任何疑问或建议?评论区就是我们的连心桥!您的每一条留言我都将认真研读,并在24小时内回复解答📝。

愿我们携手同行,在知识的雨林中茁壮成长🌳,共享思想绽放的甘甜果实。下期相遇时,期待看到您智慧的评论与闪亮的点赞身影✨!

万分感谢🙏🙏您的点赞👍👍、收藏⭐🌟、评论💬🗯️、关注❤️💚~


自我介绍:一线互联网大厂资深算法研发(工作6年+),4年以上招聘面试官经验(一二面面试官,面试候选人400+),深谙岗位专业知识、技能雷达图,已累计辅导15+求职者顺利入职大中型互联网公司。熟练掌握大模型、NLP、搜索、推荐、数据挖掘算法和优化,提供面试辅导、专业知识入门到进阶辅导等定制化需求等服务,助力您顺利完成学习和求职之旅(有需要者可私信联系)

友友们,自己的知乎账号为**"快乐星球"**,定期更新技术文章,敬请关注!

相关推荐
朝朝又沐沐2 小时前
算法竞赛阶段二-数据结构(36)数据结构双向链表模拟实现
开发语言·数据结构·c++·算法·链表
薰衣草23333 小时前
一天两道力扣(6)
算法·leetcode
逝雪Yuki3 小时前
Leetcode——287. 寻找重复数
c++·leetcode·二分查找·双指针·环形链表
艾莉丝努力练剑4 小时前
【数据结构与算法】数据结构初阶:详解排序(二)——交换排序中的快速排序
c语言·开发语言·数据结构·学习·算法·链表·排序算法
科大饭桶4 小时前
数据结构自学Day13 -- 快速排序--“前后指针法”
数据结构·算法·leetcode·排序算法·c
@蓝莓果粒茶6 小时前
LeetCode第350题_两个数组的交集II
c++·python·学习·算法·leetcode·职场和发展·c#
设计师小聂!6 小时前
力扣热题100----------53最大子数组和
java·数据结构·算法·leetcode
YouQian7727 小时前
问题 C: 字符串匹配
c语言·数据结构·算法
yanxing.D7 小时前
408——数据结构(第二章 线性表)
数据结构·算法
艾莉丝努力练剑7 小时前
【LeetCode&数据结构】二叉树的应用(二)——二叉树的前序遍历问题、二叉树的中序遍历问题、二叉树的后序遍历问题详解
c语言·开发语言·数据结构·学习·算法·leetcode·链表