Appium+Python+pytest自动化测试框架

先简单介绍一下目录,再贴一些代码,代码里有注释

Basic目录下写的是一些公共的方法,Data目录下写的是测试数据,image存的是测试失败截图,Log日志文件,Page测试的定位元素,report测试报告,Test测试用例,pytest.ini是pytest启动配置文件,requirements.txt需要安装的py模块,run.py运行文件

 

Basic/base.py

里面封装了 一些方法,元素的点击,输入,查找,还有一些自己需要的公共方法也封装在里面,如果你们有别的需要可以自己封装调用

# coding=utf-8
import random
import allure
import pymysql
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from Basic import Log
import os
 
log = Log.MyLog()
 
 
class Base(object):
    def __init__(self, driver):
        self.driver = driver
 
    # 自定义一个元素查找方法
    def find_element(self, feature,timeout=5, poll=1.0):
        # feature = By.XPATH,"//*[@text='显示']"
        """
        依据用户传入的元素信息特征,然后返回当前用户想要查找元素
        :param feature: 元组类型,包含用户希望的查找方式,及该方式对应的值
        :return: 返回当前用户查找的元素
        """
        by = feature[0]
        value = feature[1]
        wait = WebDriverWait(self.driver, timeout, poll)
        if by == By.XPATH:
            # print( "说明了用户想要使用 xpath 路径的方式来获取元素" )
            value = self.make_xpath(value)
        return wait.until(lambda x: x.find_element(by,value))
 
    def find_elements(self, feature):
        wait = WebDriverWait(self.driver, 5, 1)
        return wait.until(lambda x: x.find_elements(feature[0], feature[1]))
 
    def click_element(self, loc):
        '''
            封装点击操作函数
        '''
        self.find_element(loc).click()
 
    def input_text(self, loc, text):
        '''
            封装输入操作函数
        '''
        self.fm = self.find_element(loc)
        self.fm.clear()  # 需要先清空输入框,防止有默认内容
        self.fm.send_keys(text)
 
    # 自定义了一个可以自动帮我们拼接 xpath 路径的工具函数
    def make_xpath(self, feature):
        start_path = "//*["
        end_path = "]"
        res_path = ""
 
        if isinstance(feature, str):
 
            # 如果是字符串 我们不能直接上来就拆我们可以判断一下它是否是默认正确的 xpath 写法
            if feature.startswith("//*["):
                return feature
 
            # 如果用户输入的是字符串,那么我们就拆成列表再次进行判断
            split_list = feature.split(",")
            if len(split_list) == 2:
                # //*[contains(@text,'设')]
                res_path = "%scontains(@%s,'%s')%s" % (start_path, split_list[0], split_list[1], end_path)
            elif len(split_list) == 3:
                # //[@text='设置']
                res_path = "%s@%s='%s'%s" % (start_path, split_list[0], split_list[1], end_path)
            else:
                print("请按规则使用")
        elif isinstance(feature, tuple):
            for item in feature:
                # 默认用户在元组当中定义的数据都是字符串
                split_list2 = item.split(',')
                if len(split_list2) == 2:
                    res_path += "contains(@%s,'%s') and " % (split_list2[0], split_list2[1])
                elif len(split_list2) == 3:
                    res_path += "@%s='%s' and " % (split_list2[0], split_list2[1])
                else:
                    print("请按规则使用")
            andIndex = res_path.rfind(" and")
            res_path = res_path[0:andIndex]
            res_path = start_path + res_path + end_path
        else:
            print("请按规则使用")
 
        return res_path
 
    def assert_ele_in(self, text, element):
        '''
            封装断言操作函数
        '''
        try:
            assert text in self.find_element(element).text
            assert 0
        except Exception:
            assert 1
 
    def get_assert_text(self, element):
        ele = self.find_element(element, timeout=5, poll=0.1)
        return ele.text
 
    # 自定义一个获取 toast内容的方法
    def get_toast_content(self, message):
        tmp_feature = By.XPATH, "//*[contains(@text,'%s')]" % message
        ele = self.find_element(tmp_feature)
        return ele.text
 
    # 自定义一个工具函数,可以接收用户传递的部分 toast 信息,然后返回一个布尔值,来告诉
    # 用户,目标 toast 到底是否存在
    def is_toast_exist(self, mes):
        # 拿着用户传过来的 message 去判断一下包含该内容的 toast 到底是否存在。
        try:
            self.get_toast_content(mes)
            return True
        except Exception:
            # 如果目标 toast 不存在那么就说明我们的实际结果和预期结果不一样
            # 因此我们想要的是断言失败
            return False
 
    def get_mysql(self,  table, value):
        '''连接数据库'''
        # 打开数据库连接
        db = pymysql.connect(host='', port=, db=, user='', passwd='', charset='utf8')
        # 使用 cursor() 方法创建一个游标对象 cursor
        cursor = db.cursor()
        try:
            # 使用 execute()  方法执行 SQL 查询
            cursor.execute(value)
            db.commit()
        except Exception as e:
            print(e)
            db.rollback()
        # 使用 fetchone() 方法获取单条数据.
        data = cursor.fetchone()
        # 关闭数据库连接
        db.close()
        return data
 
    def get_xpath(self, value):
        '''封装获取xpath方法'''
        text = By.XPATH, '//*[@text="%s"]' % value
        return text
 
    # 自定义一个获取当前设备尺寸的功能
    def get_device_size(self):
        x = self.driver.get_window_size()["width"]
        y = self.driver.get_window_size()["height"]
        return x, y
 
    # 自定义一个功能,可以实现向左滑屏操作。
    def swipe_left(self):
        start_x = self.get_device_size()[0] * 0.9
        start_y = self.get_device_size()[1] * 0.5
        end_x = self.get_device_size()[0] * 0.4
        end_y = self.get_device_size()[1] * 0.5
        self.driver.swipe(start_x, start_y, end_x, end_y)
 
    # 自定义一个功能,可以实现向上滑屏操作。
    def swipe_up(self):
        start_x = self.get_device_size()[0] * 1/2
        start_y = self.get_device_size()[1] * 1/2
        end_x = self.get_device_size()[0] * 1/2
        end_y = self.get_device_size()[1] * 1/7
        self.driver.swipe(start_x, start_y, end_x, end_y, 500)
 
    # 切换到微信
    def switch_weixxin(self):
        self.driver.start_activity("com.tencent.mm", ".ui.LauncherUI")
 
    # 切换到医生端
    def switch_doctor(self):
        self.driver.start_activity("com.rjjk_doctor", ".MainActivity")
 
    # 切换到销售端
    def switch_sale(self):
        self.driver.start_activity("com.rjjk_sales", ".MainActivity")
 
    def switch_webview(self):
        # 切换到webview
        print(self.driver.contexts)
        time.sleep(5)
        self.driver.switch_to.context("WEBVIEW_com.tencent.mm:tools")
        print("切换成功")
        time.sleep(3)
 
    # 自定义根据坐标定位
    def taptest(self, a, b):
        # 设定系数,控件在当前手机的坐标位置除以当前手机的最大坐标就是相对的系数了
        # 获取当前手机屏幕大小X,Y
        X = self.driver.get_window_size()['width']
        Y = self.driver.get_window_size()['height']
        # 屏幕坐标乘以系数即为用户要点击位置的具体坐标
        self.driver.tap([(a * X, b * Y)])
 
    # 自定义截图函数
    def take_screenShot(self):
        '''
        测试失败截图,并把截图展示到allure报告中
        '''
        tm = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
        self.driver.get_screenshot_as_file(
            os.getcwd() + os.sep + "image/%s.png" % tm)
        allure.attach.file(os.getcwd() + os.sep + "image/%s.png" %
                           tm, attachment_type=allure.attachment_type.PNG)
 
    # 自定义随机生成11位手机号
    def create_phone(self):
        # 第二位数字
        second = [3, 4, 5, 7, 8][random.randint(0, 4)]
        # 第三位数字
        third = {
            3: random.randint(0, 9),
            4: [5, 7, 9][random.randint(0, 2)],
            5: [i for i in range(10) if i != 4][random.randint(0, 8)],
            7: [i for i in range(10) if i not in [4, 9]][random.randint(0, 7)],
            8: random.randint(0, 9),
        }[second]
        # 最后八位数字
        suffix = random.randint(9999999, 100000000)
        # 拼接手机号
        return "1{}{}{}".format(second, third, suffix)

 

Basic/deiver.py
APP启动的前置条件,一个是普通的app,一个是微信公众号,配置微信公众号自动化测试和一般的APP是有点区别的,微信需要切换webview才能定位到公众号

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

from appium import webdriver

def init_driver():

    desired_caps = {}

    # 手机 系统信息

    desired_caps['platformName'] = 'Android'

    desired_caps['platformVersion'] = '9'

    # 设备号

    desired_caps['deviceName'] = 'emulator-5554'

    # 包名

    desired_caps['appPackage'] = ''

    # 启动名

    desired_caps['appActivity'] = ''

    desired_caps['automationName'] = 'Uiautomator2'

    # 允许输入中文

    desired_caps['unicodeKeyboard'] = True

    desired_caps['resetKeyboard'] = True

    desired_caps['autoGrantPermissions'] = True

    desired_caps['noReset'] = False

    # 手机驱动对象

    driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

    return driver

def driver_weixin():

    desired_caps = {}

    # 手机 系统信息

    desired_caps['platformName'] = 'Android'

    desired_caps['platformVersion'] = '9'

    # 设备号

    desired_caps['deviceName'] = ''

    # 包名

    desired_caps['appPackage'] = 'com.tencent.mm'

    # 启动名

    desired_caps['appActivity'] = '.ui.LauncherUI'

    # desired_caps['automationName'] = 'Uiautomator2'

    # 允许输入中文

    desired_caps['unicodeKeyboard'] = True

    desired_caps['resetKeyboard'] = True

    desired_caps['noReset'] = True

    # desired_caps["newCommandTimeout"] = 30

    # desired_caps['fullReset'] = 'false'

    # desired_caps['newCommandTimeout'] = 10

    # desired_caps['recreateChromeDriverSessions'] = True

    desired_caps['chromeOptions'] = {'androidProcess': 'com.tencent.mm:tools'}

    # 手机驱动对象

    driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

    return driver

Basic/get_data.py
这是获取测试数据的方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

import os

import yaml

def getData(funcname, file):

    PATH = os.getcwd() + os.sep

    with open(PATH + 'Data/' + file + '.yaml', 'r', encoding="utf8") as f:

        data = yaml.load(f, Loader=yaml.FullLoader)

    # 1 先将我们获取到的所有数据都存放在一个变量当中

    tmpdata = data[funcname]

    # 2 所以此时我们需要使用循环走进它的内心。

    res_arr = list()

    for value in tmpdata.values():

        tmp_arr = list()

        for j in value.values():

            tmp_arr.append(j)

        res_arr.append(tmp_arr)

    return res_arr

Basic/Log.py
日志文件,不多介绍

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

# -*- coding: utf-8 -*-

"""

封装log方法

"""

import logging

import os

import time

LEVELS = {

    'debug': logging.DEBUG,

    'info': logging.INFO,

    'warning': logging.WARNING,

    'error': logging.ERROR,

    'critical': logging.CRITICAL

}

logger = logging.getLogger()

level = 'default'

def create_file(filename):

    path = filename[0:filename.rfind('/')]

    if not os.path.isdir(path):

        os.makedirs(path)

    if not os.path.isfile(filename):

        fd = open(filename, mode='w', encoding='utf-8')

        fd.close()

    else:

        pass

def set_handler(levels):

    if levels == 'error':

        logger.addHandler(MyLog.err_handler)

    logger.addHandler(MyLog.handler)

def remove_handler(levels):

    if levels == 'error':

        logger.removeHandler(MyLog.err_handler)

    logger.removeHandler(MyLog.handler)

def get_current_time():

    return time.strftime(MyLog.date, time.localtime(time.time()))

class MyLog:

    path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

    log_file = path+'/Log/log.log'

    err_file = path+'/Log/err.log'

    logger.setLevel(LEVELS.get(level, logging.NOTSET))

    create_file(log_file)

    create_file(err_file)

    date = '%Y-%m-%d %H:%M:%S'

    handler = logging.FileHandler(log_file, encoding='utf-8')

    err_handler = logging.FileHandler(err_file, encoding='utf-8')

    @staticmethod

    def debug(log_meg):

        set_handler('debug')

        logger.debug("[DEBUG " + get_current_time() + "]" + log_meg)

        remove_handler('debug')

    @staticmethod

    def info(log_meg):

        set_handler('info')

        logger.info("[INFO " + get_current_time() + "]" + log_meg)

        remove_handler('info')

    @staticmethod

    def warning(log_meg):

        set_handler('warning')

        logger.warning("[WARNING " + get_current_time() + "]" + log_meg)

        remove_handler('warning')

    @staticmethod

    def error(log_meg):

        set_handler('error')

        logger.error("[ERROR " + get_current_time() + "]" + log_meg)

        remove_handler('error')

    @staticmethod

    def critical(log_meg):

        set_handler('critical')

        logger.error("[CRITICAL " + get_current_time() + "]" + log_meg)

        remove_handler('critical')

if __name__ == "__main__":

    MyLog.debug("This is debug message")

    MyLog.info("This is info message")

    MyLog.warning("This is warning message")

    MyLog.error("This is error")

    MyLog.critical("This is critical message")

Basic/Shell.py
执行shell语句方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

# -*- coding: utf-8 -*-

# @Time    : 2018/8/1 下午2:54

# @Author  : WangJuan

# @File    : Shell.py

"""

封装执行shell语句方法

"""

import subprocess

class Shell:

    @staticmethod

    def invoke(cmd):

        output, errors = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()

        o = output.decode("utf-8")

        return o

Page/page.py

1

2

3

4

5

6

7

class Page:

    def __init__(self, driver):

        self.driver = driver

    @property

    def initloginpage(self):

        return Login_Page(self.driver)

Test/test_login.py
登陆的测试用,我贴一条使用数据文件的用例

1

2

3

4

5

6

7

8

9

10

11

class Test_login:

    @pytest.mark.parametrize("args", getData("test_login_error", 'data_error_login'))

    def test_error_login(self, args):

        """错误登陆"""

        self.page.initloginpage.input_user(args[0])

        self.page.initloginpage.input_pwd(args[1])

        self.page.initloginpage.click_login()

        toast_status = self.page.initloginpage.is_toast_exist(args[2])

        if toast_status == False:

            self.page.initpatientpage.take_screenShot()

            assert False

pytest.ini
pytest配置文件,注释的是启动失败重试3次,因为appium会因为一些不可控的原因失败,所有正式运行脚本的时候需要加上这个

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

[pytest]

;addopts = -s --html=report/report.html --reruns 3

addopts = -s --html=report/report.html

testpaths = ./Test

python_files = test_*.py

python_classes = Test*

python_functions = test_add_prescription_list

requirements.txt

框架中需要的患教,直接pip install -r requirements.txt 安装就可以了,可能会失败,多试几次

```python

adbutils==0.3.4

allure-pytest==2.7.0

allure-python-commons==2.7.0

Appium-Python-Client==0.46

atomicwrites==1.3.0

attrs==19.1.0

certifi==2019.6.16

chardet==3.0.4

colorama==0.4.1

coverage==4.5.3

decorator==4.4.0

deprecation==2.0.6

docopt==0.6.2

enum34==1.1.6

facebook-wda==0.3.4

fire==0.1.3

humanize==0.5.1

idna==2.8

importlib-metadata==0.18

logzero==1.5.0

lxml==4.3.4

more-itertools==7.1.0

namedlist==1.7

packaging==19.0

Pillow==6.1.0

pluggy==0.12.0

progress==1.5

py==1.8.0

PyMySQL==0.9.3

pyparsing==2.4.0

pytest==5.0.0

pytest-cov==2.7.1

pytest-html==1.21.1

pytest-metadata==1.8.0

pytest-repeat==0.8.0

pytest-rerunfailures==7.0

PyYAML==5.1.1

requests==2.22.0

retry==0.9.2

selenium==3.141.0

six==1.12.0

tornado==6.0.3

uiautomator2==0.3.3

urllib3==1.25.3

wcwidth==0.1.7

weditor==0.2.3

whichcraft==0.6.0

zipp==0.5.1

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/398034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JPCS出版|EI稳定检索】2024年第四届人工智能与工业技术应用国际学术会议(AIITA 2024)

2024年第四届人工智能与工业技术应用国际学术会议(AIITA 2024) 2024 4th International Conference on Artificial Intelligence and Industrial Technology Applications 2024年4月12日-14日 | 中国广州 大会官网:www.aiita.net 二轮截稿…

查看halcon算子的执行时间

点击这个时钟, 运行程序以后就能看到算子的消耗时间

用连续自然数之和来表达整数

文章目录 题目描述:用连续自然数之和来表达整数输入描述输出描述用例1说明用例2代码运行举例注意:1、sprintf 和 strcat区别2、qsort 题目描述:用连续自然数之和来表达整数 一个整数可以由连续的自然数之和来表示。 给定一个整数&#xff0c…

基于学习的参数化查询优化方法

一、背景介绍 参数化查询是指具有相同模板,且只有谓词绑定参数值不同的一类查询,它们被广泛应用在现代数据库应用程序中。它们存在反复执行动作,这为其性能优化提供了契机。 然而,当前许多商业数据库处理参数化查询的方法仅仅只…

大顶堆、小顶堆

堆 堆堆的维护1.自我初始化代码2.插入时维护时间复杂度 代码如有误欢迎指出 本文是最近在整理排序算法的时候写到堆排序单拎出来写的,目前只有维护代码 堆 堆是一颗完全二叉树,同时保证所有双亲都比自己的孩子大(可以相等 堆的维护 使用数…

【无标题】力扣报错:member access within null pointer of type ‘struct ListNode‘

项目场景: 做单链表反转题目,报错:member access within null pointer of type ‘struct ListNode’ 题目链接:LINK 问题描述 我明明在初始化指针时候,已经处理了n2->next情况却依然报错 这个报错提示含义是:大概就…

VNCTF2024misc方向部分wp

文章目录 sqlsharkLearnOpenGLez_msbOnlyLocalSql sqlshark tshark -r sqlshark.pcap -Y "http" -T fields -e frame.len -e http.file_data > data.txt不太像常规的盲注,一次性发送两条很类似的payload,比常规的多了一个least在判断passw…

C语言——从头开始——深入理解指针(1)

一.内存和地址 我们知道计算上CPU(中央处理器)在处理数据的时候,是通过地址总线把需要的数据从内存中读取的,后通过数据总线把处理后的数据放回内存中。如下图所示: 计算机把内存划分为⼀个个的内存单元,每…

python-pyqt5-工具按钮(QToolButton)添加菜单(QMenu)

QToolButton提供了比普通按钮更丰富的功能。它可以显示一个图标、一个文本或二者结合,还支持各种样式和行为,例如弹出菜单或多种动作模式 样式 setToolButtonStyle(Qt.ToolButtonStyle) # 设置按钮样式风格# 参数Qt.ToolButtonIconOnly …

Window系统GPT-SoVITS配置安装

GPT-SoVITS配置安装 GPT-SoVITS配置Python下载以及安装源文件安装依赖 运行整理在安装配置环境时遇到的报错总结 GPT-SoVITS配置 作者链接 Python下载以及安装 版本这里根据教程的版本走即可,这里不会安装python或者不会配置环境的参考我之前的文章 Python 3.9,…

Requests教程-11-重定向与请求历史

领取资料,咨询答疑,请➕wei: June__Go 上小节中,我们学习了requests的Session会话对象,本小节我们讲解一下requests的重定向与请求历史。 重定向的定义: 重定向(Redirect)就是通过各种方法将各种网络请求重新定个方…

STM32 TIM输入捕获测频率占空比库函数

目录 一、输入捕获初始化函数 TIM_ICInit TIM_PWMIConfig TIM_ICStructInit 二、主从触发模式对应函数 TIM_SelectInputTrigger TIM_SelectOutputTrigger TIM_SelectSlaveMode 三、配置分频器函数 TIM_SetIC1Prescaler TIM_SetIC2Prescaler TIM_SetIC3Prescaler T…

浅谈木材加工企业的电气火灾隐患及电气火灾监控系统的应用

摘要:本文分析了木材加工企业的特点、现状及常见电气火灾隐患,提出了消灭电气火灾隐患的措施。结尾介绍了木材加工企业常用电气设备的选用及电气火灾监控系统在其低压配电系统的应用方案及产品选型。 关键词:木材加工企业;电气火…

kafka的安装,用于数据库同步数据

1.0 背景调研 因业务需求,需要查询其他部门的数据库数据,不方便直连数据库,所以要定时将他们的数据同步到我们的环境中,技术选型选中了kafkaCDC Kafka是Apache旗下的一款分布式流媒体平台,Kafka是一种高吞吐量、持久…

微服务—RabbitMQ高级(延迟消息)

本博客为个人学习笔记,学习网站:2023黑马程序员RabbitMQ入门到实战教程 高级篇章节 目录 延迟消息 死信交换机 延迟消息插件 下载安装 延迟交换机声明 ​编辑 发送延迟消息 订单状态同步问题 延迟消息 在电商的支付业务中,对于一些库…

基于springboot学生就业管理系统源码和论文

随着信息化时代的到来,管理系统都趋向于智能化、系统化,学生就业管理系统也不例外,但目前国内仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,人工管理显然已无法应对时代的变化,而…

word中插入代码

可以先把代码在highlightcode 中格式化后复制插入 highlightcode地址:https://highlightcode.com/ 复制到word后效果,可以看到美观多了 原始效果

Java面试题:volatile专题

王有志,一个分享硬核Java技术的互金摸鱼侠 加入Java人的提桶跑路群:共同富裕的Java人 今天是《面霸的自我修养》第4篇文章,我们一起来看看面试中会问到哪些关于volatile的问题吧。数据来源: 大部分来自于各机构(Java之父,Java继父,某灵,某泡,某客)以及各博主整理文档…

基于uniapp微信小程序的汽车租赁预约系统

随着现代汽车租赁管理的快速发展,可以说汽车租赁管理已经逐渐成为现代汽车租赁管理过程中最为重要的部分之一。但是一直以来我国传统的汽车租赁管理并没有建立一套完善的行之有效的汽车租赁管理系统,传统的汽车租赁管理已经无法适应高速发展,…

leetcode面试题 02.07. 链表相交

leetcode面试题 02.07. 链表相交 题目 思路 方案一:使用哈希表储存一个链表节点,在另一个链表进行查询是否有相同节点方案二:统计两个链表长度,然后末尾对齐,判断是否有相同节点 代码 使用哈希表set # Definition…