300分钟搞定算法面试之常用数据结构

       很多时候看到题目,如果你认真的思考,大都会得到一个直观的思路,跟着这个思路做会得到一个结果。但这只是第一步,很多时候first thought的结果并非最优的,甚至会有很多冗余。这个时候我们不能满足于仅仅跑通代码,得到一个预期的输出就ok了。否则我们很难得到有效的进步。我认为我们要再次审题,再次审视自己的代码,看看哪些地方的计算有冗余,可以优化,看看哪些概念可以转换,很多时候你换个角度理解,就会发现不同的角度竟然可以更高效的解决相同的问题。 

     每道题目都会有多种解法,希望自己在练习时都能深入去思考,思考问题的本质在哪里。当然前提是打好基本功。

好了,唠完开始上题。以下很多题目来自leetcode

常用的数据结构 包括 数组、字符串、链表、队列、栈、树。下面会逐一回顾和举例。

一、数组

用于按顺序存储元素的集合。元素可以随机存取,因为数组中元素可通过索引访问。存取的时间复杂度为O(1)

数组可以是一维,也可以是多维的。 一维数组也称为线性数组。

以下代码简单说明数组的基本用法

1、数组定义,初始化
int[] a0 = new int[5];
int[] a1 = {1,2,3};

以上为两种初始化方法。

2、数组访问
for(int i=0;i<a0.length;i++)
     System.out.println(a1[i]);

以上数组的定义都是固定容量,即初始化时指定了数组大小。这种形式有时很不方便。

因此大多数语言都提供了内置的动态数组,它仍是随机存取的列表数据结构,但大小是可变的。

如c++ 中的vector java中的ArrayList C#中的List<> 以及python中的列表list

以下是一些数组相关的代码编程题,皆来自leetcode 

题一 

给定一个整数类型的数组 nums,请编写一个能够返回数组“中心索引”的方法。

我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。

如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

例如:

输入: 
nums = [1, 7, 3, 6, 5, 6]
输出: 3  解释 :索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。
同时, 3 也是第一个符合要求的中心索引。

为了练习python,以下全部用python语言编写

方法一 暴力破解, 遍历所有情况 ,时间复杂度O(n*n)
    def pivotIndex(nums):  
        if nums == None:
             return -1
         for i in range(len(nums)):
             left,right =0,0
             for j in range(i):
                 left += int(nums[j])
             for k in range(i+1,len(nums)):
                 right += int(nums[k])
             if left == right:
                 return i
 
         return -1
    
        #方法一可以解决问题,但显然效率太低,计算有冗余,我们要想办法减少计算冗余,提升算法效率
        #方法二 遍历优化,由于每次计算左边的和,或者右边的和时,都要进行累加,仔细思考后发现这其中的重复计算在于,当pivot向右移动时,其实不用每次都全部元素累加,只需保存上次的累加和,左边在上次的基础上加上新的值,而右边只需在上次的基础上减去一个值就可以了。
 
   def pivotIndex(nums):  
        llast,rlast = 0,sum(nums[1:])
        for i in range(len(nums)):
            if llast == rlast:
                return i
            if i == len(nums)-1:
                return -1
            llast += nums[i]
            rlast -= nums[i+1]
        return -1
        #方法三,进一步优化程序的编写,上述代码略微繁琐
    def pivotIndex(nums):
        left = 0
        total = sum(nums)
        for i,j in enumerate(nums):
            if left == (total - j )/2:
                return i
            left += j
        return -1

上述方法,一个是注意enumerate()的用法,迭代时,前面的 i 代表索引,j 代表数组中的值

题目二:至少是其他数字两倍的最大数

在一个给定的数组nums中,总是存在一个最大元素 。

查找数组中的最大元素是否至少是数组中每个其他数字的两倍。

如果是,则返回最大元素的索引,否则返回-1。

#方法一,也是最朴素的思路,首先找到这个最大值和其索引,然后遍历数组,看是否满足条件
def dominantIndex(nums):
     maxval,index = 0,-1
     for i,j in enumerate(nums):
         if maxval < j:
                 maxval = j
                 index = i
     for i,j in enumerate(nums):
          if index == i:
                 continue
          if maxval < 2*j:
                 return -1
     return index
 
#方法二 ,借助排序,也是一个常用的技巧。排序后,将最大的元素与第二大的元素进行比较,只要一次即可
if len(nums) == 1:
       return 0
nums1 = sorted(nums,reverse=True)
if nums1[0] >= 2*nums1[1]:
      return nums.index(nums1[0])
return -1

这里也有几个算法之外的注意点:1是 sort() 与 sorted()的区别。sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。sort直接修改list本身,没有返回值,sorted()不修改要排序的可迭代对象,而是返回一个排序好的新的可迭代对象。

题目三:加一

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头

示例:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123
#逻辑其实很简单,就是从低位加1,若和等于10,向高位进一位,继续计算,否则返回该数组
def plusOne(digits):
    #首先一个点,是倒叙遍历,这有点不太符合我们的常规操作,但原理是一样的
    for i in range(len(digits)-1,-1,-1):
        if digits[i] <= 8:
            digits[i] += 1
            return digits
        else:
            digits[i] = 0
    return [1]+digits

倒叙遍历,也可以通过 reversed 方法或 列表的reverse()方法倒排

二维数组类似,也可以用一维数组的形式来表示,常见例题包括螺旋矩阵、杨辉三角等。

二、字符串

字符串实际上是一个 unicode 字符数组。你可以执行几乎所有我们在数组中使用的操作。

有些语言中字符串是不可修改的,哪怕只是修改其中一个字符,也只能创建一个新的字符串。像python java都是这样。

python 可使用 replace('a','b')会返回一个新的字符串,也可先把字符串转换为可修改的字符数组或者是字符列表。修改后,再转换为字符串。python中可以使用join()函数

题目一:二进制求和

给定两个二进制字符串,返回他们的和(用二进制表示)。

输入为非空字符串且只包含数字 1 和 0

示例 :

输入: a = "11", b = "1"
输出: "100"
def addBinary(a,b):
    return bin(int(a,2)+int(b,2))[2:]

这里主要是记住两个python内置函数 int()它不单单是强制把参数转换为int类型,还可以接受第二参数,代表要转换的数是多少进制。向本题int(a,2)就是把a当做是2进制数,转换为10进制的整型。最后通过bin 把10进制数再转换为2进制。

题目二: 实现 strStr()函数

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回  -1

示例 :

输入: haystack = "hello", needle = "ll"
输出: 2
def strStr(self, haystack: str, needle: str) -> int:
        #方法一,直接调用api
        #return haystack.find(needle)
        
        #方法二,遍历
        len1 = len(haystack)
        len2 = len(needle)
        if len1 < len2:
            return -1
        start,end = 0,len2-1
        while end < len1:
            substr = haystack[start:end+1]
            if substr == needle:
                return start
            start += 1
            end += 1
        return -1

题目三:最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"

题目三:最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"
def longestCommonPrefix(strs: List[str]) -> str:
        if len(strs) == 0:
            return ""
        result = []
        index = 0
        minLen = 9999
        for item in strs:
            if len(item) < minLen:
                minLen = len(item)
        while index < minLen:
            for item in strs:
                if strs[0][index] != item[index]:
                    return "".join(result)
            result.append(strs[0][index])
            index += 1
        return "".join(result)

思想很朴素,先得到最短的字符串长度,逐个索引位置 遍历每个字符串,如果全部相等,该位置字符放入列表,若不相等直接返回列表对应的字符串

题目四:翻转字符串里的单词

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:
输入: "the sky is blue"
输出: "blue is sky the"
def reverseWords(s: str)return ' '.join(s.split()[::-1])

这里要注意,不需要指定split()的参数,我之前老是用split(' ')以为这样按空格分隔的来分裂,但这样遇到多个空格时会把空格拆成独立的元素。 而不加参数时,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等分隔。所以这里根本不需要加参数。

题目五:反转字符串中的单词 III

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

示例 1:

输入: "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc" 
def reverseWords(s: str) -> str:
        li = s.split()
        return ' '.join(map(lambda x:x[::-1],li))

题目六:移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: 
[0,1,0,3,12]

输出: 
[1,3,12,0,0]
 #解法一,快慢指针
def moveZeroes(nums) -> None:
        slow = 0
        for fast in range(len(nums)):
            if nums[fast] != 0:
                nums[slow], nums[fast] = nums[fast], nums[slow]
                slow += 1
def MoveZero(nums):
    for num in nums:
        if num == 0:
            nums.remove(0) #remove方法删除列表中第一个0元素
            nums.append(0) #末尾加一个0

上面是两种常用的解题方法,方法一是经典的快慢指针,快指针寻找到非0的元素就与慢指针交换元素值。

方法二,在循环中删除和添加,有些语言是不支持的,这点python的设计很神奇

题目 七 :有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true
示例 2:

输入: s = "rat", t = "car"
输出: false
def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False
        dic1,dic2 = {},{}
        for item in s:
            if item not in dic1:
                dic1[item] = 1
            else:
                dic1[item] += 1
          
        for item in t:
            if item not in dic2:
                dic2[item] = 1
            else:
                dic2[item] += 1
        
        if dic1 == dic2:
           return True
        else:
            return False

其实有很多种解法,上面这种是借助字典。

242 数组 字符串 

24/25链表

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 首先,为了使用多数据源和分布式事务,我们需要添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.6</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency> ``` 接下来,我们需要在application.properties文件中配置数据源和事务管理器: ```properties # 配置主数据源 spring.datasource.url=jdbc:mysql://localhost:3306/main_db?characterEncoding=utf8&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # 配置从数据源 spring.datasource.slave.url=jdbc:mysql://localhost:3306/slave_db?characterEncoding=utf8&useSSL=false spring.datasource.slave.username=root spring.datasource.slave.password=root spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver # 配置Mybatis mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.example.entity # 配置Druid数据源 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initial-size=1 spring.datasource.druid.max-active=10 spring.datasource.druid.min-idle=1 spring.datasource.druid.max-wait=60000 spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.min-evictable-idle-time-millis=300000 spring.datasource.druid.test-while-idle=true spring.datasource.druid.test-on-borrow=false spring.datasource.druid.test-on-return=false spring.datasource.druid.filters=stat,wall,log4j spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 配置事务管理器 spring.transaction.default-timeout=600 spring.transaction.rollback-on-commit-failure=true spring.transaction.allow-bean-definition-overriding=true spring.transaction.jta.registry-name=atomikos spring.jta.enabled=true spring.jta.atomikos.connectionfactory.min-pool-size=5 spring.jta.atomikos.connectionfactory.max-pool-size=10 spring.jta.atomikos.connectionfactory.borrow-connection-timeout=30 spring.jta.atomikos.connectionfactory.max-idle-time=60 spring.jta.atomikos.connectionfactory.concurrency-level=100 ``` 然后,我们需要创建两个数据源的配置类,分别为主数据源和从数据源: ```java @Configuration @MapperScan(basePackages = "com.example.mapper.main", sqlSessionTemplateRef = "mainSqlSessionTemplate") public class MainDataSourceConfig { @Bean(name = "mainDataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource mainDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "mainSqlSessionFactory") public SqlSessionFactory mainSqlSessionFactory(@Qualifier("mainDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/main/*.xml")); return bean.getObject(); } @Bean(name = "mainTransactionManager") public DataSourceTransactionManager mainTransactionManager(@Qualifier("mainDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "mainSqlSessionTemplate") public SqlSessionTemplate mainSqlSessionTemplate(@Qualifier("mainSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } } ``` ```java @Configuration @MapperScan(basePackages = "com.example.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate") public class SlaveDataSourceConfig { @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "slaveSqlSessionFactory") public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml")); return bean.getObject(); } @Bean(name = "slaveTransactionManager") public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "slaveSqlSessionTemplate") public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } } ``` 最后,我们需要在事务管理器上添加注解@EnableTransactionManagement,并在需要使用事务的方法上添加注解@Transactional: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Transactional(rollbackFor = Exception.class, transactionManager = "mainTransactionManager") @Override public void save(User user) { userMapper.insert(user); } @Transactional(rollbackFor = Exception.class, transactionManager = "slaveTransactionManager") @Override public User findById(int id) { return userMapper.selectByPrimaryKey(id); } } ``` 以上就是使用SpringBoot+Mybatis+druid多数据源和分布式事务的基本步骤。 ### 回答2: Spring Boot是一个用于构建独立的、生产级的应用程序的框架。它简化了应用程序的开发过程,并通过自动配置来减少了繁琐的配置。MyBatis是一个ORM(对象关系映射)框架,它提供了将数据库操作映射到Java对象的功能。Druid是一种高性能的数据库连接池。 要在Spring Boot中使用MyBatis和Druid进行多数据源配置和分布式事务管理,可以按照以下步骤进行操作: 1. 添加依赖:在项目的pom.xml文件中,添加Spring Boot、MyBatis和Druid的依赖。 2. 配置数据源:在application.properties文件中,配置并命名多个数据源,设置数据库连接等信息。 3. 创建数据源配置类:创建一个配置类,使用@Configuration注解将其标记为配置类,并使用@ConfigurationProperties注解将数据源属性注入。 4. 创建数据源:根据配置类中的属性,创建多个数据源,并将其加入到数据源路由器中。 5. 配置MyBatis:创建一个配置类,使用@MapperScan注解设置MyBatis的mapper接口路径,并将数据源注入到SqlSessionFactory中。 6. 配置分布式事务:使用@EnableTransactionManagement注解启用事务管理,并配置事务管理器。 7. 编写数据库操作代码:在mapper接口中定义数据库操作方法,并在Service层中调用这些方法进行数据库操作。 通过以上步骤,你就可以在Spring Boot项目中完成MyBatis和Druid的多数据源配置和分布式事务管理。不过需要注意的是,使用多数据源和分布式事务会增加项目的复杂性和性能开销,所以在使用之前需要仔细考虑是否真正需要这些功能。 ### 回答3: Spring Boot是一种快速构建Java应用程序的框架,MyBatis是一种流行的Java持久化框架,Druid是一种高性能的数据库连接池。本文将介绍如何在Spring Boot中使用MyBatis和Druid来实现多数据源和分布式事务。 要使用多个数据源,我们首先需要配置多个数据源。在Spring Boot中,我们可以通过在application.properties或者application.yml文件中配置多个数据源的连接信息。我们需要为每个数据源指定不同的URL、用户名和密码。然后,我们可以使用@Primary和@Qualifier来指定主数据源和其他数据源。 在配置数据源后,我们需要配置MyBatis来使用这些数据源。我们可以通过创建多个SqlSessionFactory来实现多数据源,然后在每个SqlSessionFactory中设置相应的数据源。我们还可以使用@MapperScan注解来自动扫描和注册Mapper接口。 在使用MyBatis和多个数据源时,我们可能会遇到事务管理的问题。为了解决这个问题,我们可以使用Spring Boot提供的@Transactional注解来标记需要进行事务管理的方法,然后Spring Boot会自动为我们处理事务。对于需要跨多个数据源进行事务管理的情况,我们可以使用JTA(Java Transaction API)实现分布式事务。在Spring Boot中,我们可以使用Atomikos或Bitronix等JTA提供商来实现分布式事务。 总结起来,使用Spring Boot、MyBatis和Druid,我们可以很容易地实现多数据源和分布式事务。通过正确配置数据源和使用相关注解,我们可以在几分钟内完成这些任务。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值