数据结构和算法简介

  • 数据结构

    就是存储和组织数据的方式, 分为: 线性结构非线性结构

  • 算法

    就是解决问题的思路和发放, 它具有独立性, 即: 它不依赖语言, 而是解决问题的思路. Java能做, Python也能做.

    • 特性

      有输入, 有输出, 有穷性, 确定性, 可行性.

    • 如何衡量算法的优劣

      ==大O标记法,== 即: 将次要条件都省略掉, 最终形成1个表达式.

      **主要条件:**随着问题规模变化而==变化==的.

      **次要条件:**随则问题规模变化而==不变==的.

      1742438995269

    • 最优和最坏时间复杂度

      如非特殊说明, 我们考虑的都是 最坏时间复杂度, 因为它是算法的一种保证.

      而最优时间复杂度是算法的 最理想, 最乐观的状况, 没有太大的参考价值.

    • 常见的时间复杂度如下

      从最优到最坏分别是:

      O(1) -> O(logn) -> O(n) -> O(n logn) -> O(n²) -> O(n³)

      常数阶 -> 对数阶 -> 线性阶 -> 线性对数阶 -> 平方阶 -> 立方阶

    • 常见的空间复杂度如下

      了解即可, 因为服务器(内存)资源一般是足够的

      从最优到最坏分别是:

      O(1) -> O(logn) -> O(n) -> O(n²) -> O(n³)

数据结构分类

  • 分类

    数据结构 = 存储, 组织数据的方式, 是算法解决问题时的载体.

    • 线性结构

      特点: 每个节点都只能有1个前驱, 1个后继节点.

      例如: 顺序表(栈, 队列), 链表

    • 非线性结构

      特点: 每个节点都可以有多个前驱, 多个后继节点.

      例如: 树, 图

线性结构存储数据的方式

  • 图解

    1742449680820

  • 顺序表存储方式详解

    • 一体式存储

      1742449752895

    • 分离式存储

      1742449775956

顺序表的存储方式

  • 解释

    顺序表有 数据区 和 信息区两部分组成.

  • 特点

    • 数据区 和 信息区在一起的 -> 一体式存储(扩容时只能整体搬迁)
    • 数据取货 和 信息区分开存储的 -> 分离式存储(可以直接让信息区指向新的 数据区即可, 不用整体搬迁).
  • 顺序表扩容策略

    思路1: 每次增加固定的容量. 拿时间换空间

    思路2: 每次扩容, 容量翻倍. 拿空间换时间

线性结构之链表介绍

  • 概述

    链表属于 线性结构, 在存储时 不要求连续的内存空间, 只要有地儿就行.
    可以简单理解为, 它是用来解决顺序表的弊端的(必须要有足够的连续空间, 否则扩容失败.)

  • 组成

    链表 由 节点 组成. 节点又分为 数值域(元素域) 和 地址域(链接域), 根据节点不同, 链表可以分为 四大类.

  • 图解

    1743163476385

    1743163490026

    1743163501025

    自定义代码模拟链表

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
"""
1:链表介绍
概述:
他是数据结果的线性结构的一种,每一个阶段只有一个前驱和一个后继
作用:
优化顺序表的弊端(如果没有足够的内存空间,会导致扩容失败)
链表的扩容,有地就行。连不连续无所谓
组成:
链表 有 节点 组成 ,节点由 元素域 和链接域 组成
分类:
单链表
单循环链表
双链表
双循环链表

自定义代码模拟链表 思路分析:
1:自定义SingleNode类 表示节点类
属性:
item 数值域(元素域)
next 地址域(链接域) 不是他的地址,而是他的下一个节点地址
2:自定义SingleLinkedList类 表示:链表
属性:
head 表示头结点 ,指向链表的第一个节点
行为:
is_empty(self) :链表是否为空
length(self):判断链表长度
traverse(self):遍历整个链表
add(self,item) :给链表头部添加元素
append(self,item):链表尾部添加元素
insert(self,item):指定位置添加元素
remove(self,item):删除节点
search(self,item):查询节点是否存在
"""


# 自定义SingleNode类,表示节点类
class SingleNode:
# 初始化属性
def __init__(self, item):
self.item = item # 元素域(数值域)
self.next = None # 链接域(地址域)


# 自定义SingleLinkList类,表示链表
class SingleLinkList:
def __init__(self, node=None):
self.head = node

# is_empty 链表是否为空
def is_empty(self):
# 思路:判断头结点是否为None,如果为None,则链表为空
# 写法一 if else
# if self.head is None:
# return True
# else:
# return False
# 写法二
# return True if self.head is None else False
# 写法三(推荐)
return self.head is None

def length(self): # 判断链表长度
# 默认从头开始
cur = self.head
# 初始化计数器
count = 0
# 开始遍历 ,只要当前节点不为空,就一直循环
while cur is not None:
count += 1
cur = cur.next
return count

def traverse(self): # 遍历整个链表
# 找到默认开头节点
cur = self.head
# 定义循环判断
while cur is not None:
print(f"数值域:{cur.item}", end="")
cur = cur.next

def add(self, item): # 给链表头部添加元素
# 1、创建一个新的节点
new_node = SingleNode(item)
# 设置新节点的地址域,指向头节点
new_node.next = self.head
# 设置头节点指向新节点
self.head = new_node

def append(self, item): # 链表尾部添加元素
new_node = SingleNode(item)
if self.is_empty():
self.head = new_node
else:
# 走到这里,说明链表不为空,需要找到链表的尾节点
# 默认从头开始
cur = self.head
while cur.next is not None:
# 地址后移
cur = cur.next
# 走到这里,cur就是最后一个节点,所以我们设置它的地址指向新的节点
cur.next = new_node

def insert(self, pos, item): # 指定位置添加元素
# 头插
if pos <= 0:
self.add(item)
# 尾插
elif pos >= self.length():
self.append(item)
else: # 任意位置
# 走到这里,说明pos给合法的,即:中间值,想到找到插入到哪个元素
cur = self.head
# 定义当前节点的位置
count = 0
while count < pos - 1:
# 走到这里,说明还没有找到插入前的那个节点,就节点后移,计数器+1
cur = cur.next
count += 1
# 走到这里,cur就是插入位置前的那个节点,封装新节点
new_node = SingleNode(item)
# 设置新节点的地址域,指向插入位置前的那个节点的地址域
new_node.next = cur.next
# 设置插入位置前的那个节点的地址域指向新节点
cur.next = new_node

def remove(self, item): # 删除节点
# 默认从头节点开始
cur = self.head
pre = None
while cur is not None:
# 判断当前节点是否是要删除的节点
if cur.item == item:
# 判断要删除的节点是否是头节点
if cur == self.head:
# 直接设置头结点为当前节点的下一个节点
self.head = cur.next
else:
# 走到这里,说明要删除的节点不是头节点,直接设置前驱(pre)的地址域指向当前节点的地址域即可
pre.next = cur.next
cur.next = None
# 走到这里,说明删除成功,返回即可
return
else:
# 走到这里,说明当前节点不是要删除的节点,后移,pre也要后移
pre = cur
cur = cur.next

def search(self, item): # 查询节点是否存在
# 默认从头节点开始
cur = self.head
# 只要当前节点不为空,就一直循环
while cur is not None:
if cur.item == item:
return True
# 如果当前节点不是要找的节点,后移
cur = cur.next
# 走到这里,所以节点就都找完了,还没有找到 ,返回False
return False


if __name__ == '__main__':
# 1.1测试节点类
node1 = SingleNode(10)
# 1.2 打印当前节点的元素域 和链接域
print(f"元素域(数值域):{node1.item}")
print(f"链接域(地址域):{node1.next}")
print(f"node1对象{node1}")
print(f"node1类型{type(node1)}")
print("---------------测试链接类-------------------")
# 2.1 测试链接类
my_linklist = SingleLinkList(node1)
print(f"头结点:{my_linklist}")
print(f"头节点的元素域{my_linklist.head.item}") # 10
print(f"头节点的地址域{my_linklist.head.next}") # None
print("---------------测试链表是否为空-------------------")
# 3.1测试链表是否为空
print(my_linklist.is_empty())
print("---------------测试头插-------------------")
my_linklist.add(20)
my_linklist.add(30)
print("---------------测试尾插-------------------")
my_linklist.append(40)
my_linklist.append(50)
print("---------------测试任意位置插入-------------------")

my_linklist.insert(-1, 2)
my_linklist.insert(8, 55)
my_linklist.insert(2, 32)

print("---------------测试删除-------------------")
my_linklist.remove(2)
my_linklist.remove(30)
my_linklist.remove(60)
print("---------------测试查找-------------------")
print(my_linklist.search(2))
print(my_linklist.search(32))

print("---------------测试链表长度-------------------")
print(f"当前链表长度为:{my_linklist.length()}")
print("---------------测试遍历-------------------")
my_linklist.traverse()