题目描述与分析
1.描述
题目描述:
构建一个单向链表,链表中包含一组整数数据。输出链表中的所有元素。
要求:
1.使用自定义的链表数据结构
2.提供一个 linkedList 类来管理链表,包含构建链表和输出链表元素的方法
3.在 main 函数中,创建一个包含一组整数数据的链表,然后调用链表的输出方法将所有元素打印出来
输入描述:
包含多组测试数据,输入直到文件尾结束。
每组的第一行包含一个整数 n,表示需要构建的链表的长度。
接下来一行包含 n 个整数,表示链表中的元素。
输出描述:
每组测试数据输出占一行。
按照顺序打印出链表中的元素,每个元素后面跟一个空格
2.分析
之前学习的字符串和数组(列表)有着以下共同点:
元素按照一定的顺序来排列
可以通过索引来访问数组中的元素和字符串中的字符
但是它们也都有着一些缺点:
1.固定大小:数组的大小通常是固定的,一旦分配了内存空间,就难以动态地扩展或缩小,如果需要存储的元素数量超出了数组的大小,就需要重新分配更大的数组,并将原来数组的内容复制过去,需要执行很多额外的操作(Python中的列表是动态扩容的,但是在大多数语言中,数组的大小固定)。
2.内存是连续的:正是因为元素按照一定的顺序来排列,它们在计算机内存中的存储也是连续的,这也就意味着,当需要存储一些需要占用空间较大的内容,也只能找一些大块的内存区域,而空间比较小的内存区域就被浪费了,从而导致了内存资源浪费。
3.固定的数据类型:数组要求所有元素具有相同的数据类型,字符串存储的都是字符,如果需要存储不同类型的数据,数组和字符串就显得无能为力了。
还有重要的一点是,如果我们想要往数组中新增加或者删除一个元素,会特别麻烦!
比如下面的图例,想要往数组中删除第三个元素,当完成删除后,还需要从删除元素位置遍历到最后一个元素位置,分别将它们都向前移动一个位置,也就是说后续的所有元素都要改变自己的位置,这是十分耗时的操作。
要想解决上述问题,需要用到链表
一、链表
链表是一种常用的数据结构,用于存储元素的集合,但它与数组(如Python中的列表)在存储和管理数据的方式上有所不同。在链表中,数据元素按线性顺序排列,但不必在内存中连续存放。每个元素都包含其自身的数据和指向列表中下一个元素的引用(或链接)。
链表的第一个节点的存储位置被称为头节点,然后通过next指针找到下一个节点,直到找到最后一个节点,最后一个节点的next指针并不存在,也就是“空”的,在 Python 中,用None来表示。
链表的种类
单向链表:每个元素包含数据和指向下一个元素的链接。
双向链表:每个元素包含数据和两个链接,一个指向下一个元素,另一个指向前一个元素。
循环链表:链表中的最后一个元素指向第一个元素。
二、面向对象
Python是一门面向对象的编程语言, 这里的“对象”实际上是对现实世界中所存在的事物的一种抽象,举个例子,你在计算机世界中怎么表示“人”这个概念呢?
人拥有着一些静态的特征,比如身高、体重、性别等,也拥有一些动态的行为,比如吃法,睡觉等,而在计算机世界中,我们将之抽象为一个类Person, 并具有与之对应的“属性”和“方法”。
“属性”表示Person类所具有的特征,比如姓名、年龄、性别,通过这些特征,我们可以描述一个“人”的基本状态。
“方法”表示Person类的行为和功能,比如吃饭、睡觉、行走,通过这些动作,我们可以描述一个“人”的动态行为。
比如下面的“伪代码”表示Person类的定义, 它包括姓名、性别、年龄和吃饭的方法。
Person:
// 姓名、性别、年龄等属性
name
gender
age
// 吃饭的方法
eat
// 行走的方法
walk
}
这样在计算机世界,一个“人”的概念就建立起来了,但这里的Person类只是个“模具”,空有概念,而无法表示具体的某一个人,只有创造一个类的实例(也就是我们说的对象),比如"张三,18, 男", “李明、20,男”,才能真正的使用。
# 实例化一个类,从而创造一个对象,和调用一个函数类似
person1 = Person("Tom", 10, "男") # person1是一个对象,是Person类的实例
person2 = Person("Jerry", 3, "男")# person2是一个对象,是Person类的实例
面向对象编程的基本概念
类(Class):类是创建对象的模板或蓝图。它定义了对象的数据和行为(即属性和方法)。
对象(Object):对象是类的实例。如果类被视为蓝图,则一个对象是根据这些蓝图构建的房子。
属性(Attributes):属性是对象的数据部分,是类中定义的变量。
方法(Methods):方法是对象可以执行的操作。在类的定义中,方法就是函数。
三、Class类
在Python中,类的定义是从class关键字开始的, 类的名称命名要使用大驼峰来命名(即MyName这种形式)
class Person:
在类的代码块中,我们可以定义变量和函数,在类中所定义的变量,将会成为所有的实例的属性,所有实例都可以访问这些变量,在类中也可以定义函数(被称之方法),类的所有实例也可以访问这些方法。
class Person:
# 类中定义一个变量name, 会成为所有实例的公共属性
name = "Tom"
# 类中也可以定义函数,称之为方法,方法也可以通过该类的实例来访问
def say_hello(self):
print("Hello")
# 创建Person的实例
person1 = Person()
person2 = Person()
# 实例可以调用属性
person1.name = "Jerry"
person2.name = "Mike"
# 实例可以调用方法
person1.say_hello()
person2.say_hello()
类中的方法每次被调用时,Python都会自动帮我们传递一个参数,表示调用方法的对象本身(也就是创建的实例),一般我们将这个参数命名为self。
上面例子中我们在类中定义了属性,以方便实例使用,但这样会把属性的“值”固定,这样导致我们创建的person1、person2实例的name值都一样,但是不同的对象实例,其属性值应该是不同的,这就需要用到特殊方法_init_,用来初始化属性
class Person:
# 定义__init__方法,name是创造实例时传递的参数
def __init__(self, name):
# self表示实例本身,即实例.name属性 = 传递的name值
self.name = name
__int__方法会在实例创建的时候立即执行,可以向新创建的对象初始化属性,调用类创建对象时,类后边的所有参数都会依次传递到init中。
# "Mike"值传递给__init__(self, name)中的name, 进行通过self.name = name给实例添加name属性
person1 = Person("Mike")
经过上面知识的学习,我们已经知道Python中类的基本结构是如下形式的: