2024.8.23 Python,类与对象,解决实际问题,设计停车系统,不用内部函数定义字典,设计地铁系统

1.设计停车系统

请你给一个停车场设计一个停车系统。停车场总共有三种不同大小的车位:大,中和小,每种尺寸分别有固定数目的车位。
请你实现 ParkingSystem 类:
ParkingSystem(int big, int medium, int small) 初始化 ParkingSystem 类,三个参数分别对应每种停车位的数目。
bool addCar(int carType) 检查是否有 carType 对应的停车位。 carType 有三种类型:大,中,小,分别用数字 1, 2 和 3 表示。一辆车只能停在 carType 对应尺寸的停车位中。如果没有空车位,请返回 false ,否则将该车停入车位并返回 true 。
输入:
[“ParkingSystem”, “addCar”, “addCar”, “addCar”, “addCar”]
[[1, 1, 0], [1], [2], [3], [1]]
输出:
[null, true, true, false, false]

这个题是一个实际应用的题,其实让我去做这个事情很简单,但是现在让我去设计一个系统,结合具体的东西去抽象化的设计参数,并去运用他成了一个难题,我不知道如何才能具象物体抽象化,并且在外部实例化并调用,所以下面这个例子代码如下:

class ParkingSystem:
	def __init__(self,big:int,medium:int,small:int):
		self.num_dic={1:big,2:medium,3:small}

	def addCar(self,carType:int)->bool:
		if self.num_dic[carType]>0:
			self.num_dic[carType]-=1
			return True
		return False

obj=ParkingSystem(big,medium,small)
param_1=obj.addCar(carType)

2.设计哈希映射

不使用任何内建的哈希表库设计一个哈希映射(HashMap)。
实现 MyHashMap 类:
MyHashMap() 用空映射初始化对象
void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的值 value 。
int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 -1 。
void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value 。

我看到这个题的第一反应是,既然是int和int的字典,那我就把字典搞成数组,你出现的最大的键就是我的数组的大小,但是我当时这样想完就否决了,这样太大了,但是答案的第一个办法就是这样写的

class MyHashMap:
	def __init__(self):
		MX=1000001
		self.vt=[-1]*MX

	def put(self,key:int,value:int)->None:
		self.vt[key]=value

	def get(self,key:int)->int:
		return self.vt[key]

	def remove(self,key:int)->None:
		self.vt[key]=-1

果然,答案说这样的方法用空间换时间,但是这样对于内存要求过大,没必要,我们应该在空间和时间上折中,用接下来的代码进行操作:

MX=1000
class MyHashMap:
	def __init__(self):
		self.vt=[[] for _ in range(MX)]

	def hash(self,key:int)->int:
		return key%MX

	def put(self,key:int,value:int)->None:
		h=self.hash(key)
		for node in self.vt[h]:
			if node[0]==key:
				node[1]=value
				return
		self.vt[h].append([key,value])
	def get(self, key: int) -> int:
        for k, v in self.vt[self.hash(key)]:
            if k == key:
                # key存在,返回value
                return v
        return -1  # key不存在,返回-1

    def remove(self, key: int) -> None:
        h = self.hash(key)
        for i, (k, v) in enumerate(self.vt[h]):
            if k == key:
                self.vt[h].pop(i)   # key存在,移除
                return

这个逻辑非常的好,chat把这个东西称为桶,我的理解就是分组存,说到底还是得存,所以就索性用批量存储,既然给了key,那我就先处理一下,给key除以1000的余数,那就给这么多键值对分成1000组里,要找的时候,我先判断一下大致在哪个桶中,然后再进行遍历寻找操作。

需要注意的是,在这个代码中,有四种对for循环的使用,这个题其实不重要,但是他对于for循环的理解到达了出神入化的程度:
1.self.vt=[[] for _ in range(MX)]这个代码是列表套列表的初始化,大列表是整个库,而中列表则有MX个,每个中列表最开始都是空的,那么这样使用for就生成了MX个空列表
2.for node in self.vt[h] 这个for循环不是遍历i或者别的,他就是在遍历中列表self.vt[h]中的每一个小列表元素,所以他不需要range,直接in就好了,这里的node代表[k,v],node[0]代表k
3.for k,v in self.vt[self.hash(key)] 这里仍然是遍历中列表中的小列表,只不过直接使用k,v遍历
4.for i,(k,v) in enumerate(self.vt[h]) 我对enumerate的理解就是,要同时遍历下标和元素的时候才会用到这个东西
总结:遍历元素,直接in,遍历下标,用range(,)注意[::]这种用法是直接用在列表里的。遍历下标加元素,用enumerate

3.__init__是什么,如何理解

在做了两道题三个方法之后,我们能看到,__init__函数的目标就是初始化,就是说列表的初始化,数据的初始化,第一个题是有通过输入来存东西的,存完东西初始化才算完成,所以这个时候他就需要在self的右边进行数据的定义。第二个题是一直是动态存进,所以最开始的初始化只要准备好空的列表就好了。

4.设计地铁系统

今天就是加强一下类与对象的训练,我不一定能完全掌握
地铁系统跟踪不同车站之间的乘客出行时间,并使用这一数据来计算从一站到另一站的平均时间。
实现 UndergroundSystem 类:
void checkIn(int id, string stationName, int t)
通行卡 ID 等于 id 的乘客,在时间 t ,从 stationName 站进入
乘客一次只能从一个站进入
void checkOut(int id, string stationName, int t)
通行卡 ID 等于 id 的乘客,在时间 t ,从 stationName 站离开
double getAverageTime(string startStation, string endStation)
返回从 startStation 站到 endStation 站的平均时间
平均时间会根据截至目前所有从 startStation 站 直接 到达 endStation 站的行程进行计算,也就是从 startStation 站进入并从 endStation 离开的行程
从 startStation 到 endStation 的行程时间与从 endStation 到 startStation 的行程时间可能不同
在调用 getAverageTime 之前,至少有一名乘客从 startStation 站到达 endStation 站
你可以假设对 checkIn 和 checkOut 方法的所有调用都是符合逻辑的。如果一名乘客在时间 t1 进站、时间 t2 出站,那么 t1 < t2 。所有时间都按时间顺序发生。

class UndergroundSystem:

    def __init__(self):
        self.tripRecords={}
        self.startStations={}	
    def checkIn(self, id: int, stationName: str, t: int) -> None:
        self.startStations[id]=(stationName,t)						#这里的()可以换成[]都可以,元组类型不可修改,但是后面只读所以可换
    def checkOut(self, id: int, stationName: str, t: int) -> None:
        startStations,startTime=self.startStations.pop(id)			#注意这里是pop不是直接去用startStation[id]去读,原因是因为你这次出站以后记录就消了
        time=t-startTime
        if startStations not in self.tripRecords:						#这里在tripRecords里找有没有startStations没有就初始化一个
            self.tripRecords[startStations]={}
        if stationName not in self.tripRecords[startStations]:
            self.tripRecords[startStations][stationName]=[0,0]		#这里的[]不可以换()因为后面要进行加减
        self.tripRecords[startStations][stationName][0]+=1
        self.tripRecords[startStations][stationName][1]+=time
    def getAverageTime(self, startStation: str, endStation: str) -> float:
        num=self.tripRecords[startStation][endStation][0]
        times=self.tripRecords[startStation][endStation][1]
        return times/num
# Your UndergroundSystem object will be instantiated and called as such:
# obj = UndergroundSystem()
# obj.checkIn(id,stationName,t)
# obj.checkOut(id,stationName,t)
# param_3 = obj.getAverageTime(startStation,endStation)

这个题难度也是不大的,但是整体的思路还是需要梳理一下,就是说我们拿到一个实际问题的时候,去抽象概念具体化还是需要一些经验的,我将写出整个的设计逻辑和思路
首先我们需要记录进站,出站,时间,人物(id)这些数据如何存是一个问题,答案给了我们一种多维字典的概念,字典的值也可以使用列表,也就是说我们的选择就会变得很多,我们在之后的代码中完全可以把入的站和出的站都变成字典的键,同时变成键,那么从这个角度上来说,我们就是需要确定谁能写进去,处理办法是继续建立一个字典,用id当成键,然后把进的时间点和站名变成时间点,存起来,在之后处理的时候进行操作。这是整体的思路。
代码逻辑如下:
1.checkIn函数,用以id为键,入站和入时间点的列表或者元组作为值,存
2.在checkOut函数中,id是联系入和出的纽带,这里的pop很重要,我最开始的时候写的是checkIn[id]带出,但是这样的话,同样一个人如果再次进去的话,是不是就乱了,所以这里要使用pop(id)把同样id的人跑出来,这样入出站,入出时间点都有了,那么接下来就进行计算和存的操作
3.计算用时time,最重要的是判断和初始化,这里的初始化就非常好的体现了什么是多维字典的深刻内涵,他首先先检查这个字典里有没有关于起点的信息,如果没有说明是新的,那么就给self.tripRecord[startStations]={}
4.然后再看endStations在不在,这个操作说明,二维的字典是键的二维,他要在self.tripRecord[startStations]里找有没有这个第二维的键,没有就初始化,self.tripRecord[startStations][endStations]=[0,0]
5.这里的可读性其实有点差,所以注释里要写一下, 前面这个0可以是同样的路线出现的次数,后面+=1即可,后面这个0是总时间sum_time那么就+=time,自此,每一条的处理和存储就已经完毕了
6.平均时间的计算,那么这就很简单了,在self.tR[s][e]直接输出想要的数字计算即可,别忘了return
注意事项
1.元组,在前面的代码中,值可以是元组,也可以是列表,前面的代码,中间只是为了读取,那么就可以使用元组进行存储也就是(),但是后面的00,就只能使用列表,元组不可修改
2.字典的多维,[][]和列表的多维很像,只不过行列的标签换成了自定义的,非常的方便
3.self.dict(),这里是一个很重要的东西,为什么全程定义的字典都加了self。那是因为,如果在函数里去定义不加self 的字典,只能在这个函数里使用,不会修改外部init定义的dict,那么要想修改外部的dict就需要给所有的dict加上self。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值