1.树
(1)链式存储
链式存储此二叉树,从根结点开始,将各个结点以及其左右子的地址使用链表进行存储。
其左子的结点编号为
2*i
,右子编号为
2*i+1
设完全二叉树的结点数为
n
,某结点的编号为i
。当
i>1
时(不是根结点时),有父节点,其编号为i//2
。当
2*i <= n
时,有左子,其编号为2*i
,否则没有左子,没左子一定没右子,其本身为叶节点。当
2*i+1 <= n
时,有右子,其编号为2*i+1
,否则就没有右子。
整体代码:
class TreeNode:
# 二叉树的创建
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
def create_bitree(n, i): # n = 3
root = TreeNode(i)
if 2 * i <= n:
root.left = create_bitree(n, 2 * i)
else:
root.left = None
if 2 * i + 1 <= n:
root.right = create_bitree(n, 2 * i + 1)
else:
root.right = None
return root
# 先序遍历——根左右
def pre_order(r):
if r is None:
return
# 根
print(r.data, end='')
# 左
pre_order(r.left)
# 右
pre_order(r.right)
return
# 中序遍历——左根右
def in_order(r):
if r is None:
return
# 左
in_order(r.left)
# 根
print(r.data, end='')
# 右
in_order(r.right)
#后序遍历——左右根
def post_order(r):
if r is None:
return
# 左
post_order(r.left)
# 右
post_order(r.right)
# 根
print(r.data, end='')
n = 3 # 树的节点(编号从1号开始)
root = create_bitree(n, 1)
print("前序遍历:")
pre_order(root)
print("\n中序遍历:")
in_order(root)
print("\n后序遍历:")
post_order(root)
输出:
前序遍历:
123
中序遍历:
213
后序遍历:
231
Process finished with exit code 0
(2)层序遍历(后边都是了解扩展)
队列思想
2.链表
(1)双向链表
概念
- 逻辑结构:线性结构
- 物理结构:链式存储结构
步骤:
- 判错
- 创新
- 尾插
- 中间插
- 中间插前半段
- 中间插后半段
- 无论前半段还是后半段,伪指针指向插入位置
post
结点后,其插入代码都是一样的
①插入
②打印
遍历双向链表
- 从前往后
- 从后往前
③删除
整体代码
class Node:
def __init__(self, data=None):
self.data = data # 数据域
self.prior = None # 指向前一个节点的指针
self.next = None # 指向下一个节点的指针
class DoubleLinkedList:
def __init__(self):
self.head = Node() # 头节点,作为哑节点(不存储实际数据)
self.tail = self.head # 尾指针初始时指向头节点
self.len = 0 # 当前链表的长度
def insert(self, position, data):
# 容错判断
if position < 0 or position > self.len:
print("插入位置无效!")
return -1
# 创建一个新的节点
new_node = Node(data)
# 将节点链接到链表中
if position == self.len: # 插入到链表尾部
self.tail.next = new_node
new_node.prior = self.tail
self.tail = new_node # 更新尾指针
else: # 插入到链表中间或头部
if position < self.len // 2: # 插入位置在前半部分,从头向后遍历
current = self.head
for _ in range(position + 1):
current = current.next
else: # 插入位置在后半部分,从尾向前遍历
current = self.tail
for _ in range(self.len - position - 1):
current = current.prior
# 进行插入操作(先连前面,再连后面)
new_node.prior = current.prior
current.prior.next = new_node
new_node.next = current
current.prior = new_node
self.len += 1 # 链表长度加1
return 0
# 删除双向链表指定位置的数据
def delete(self, position):
# 容错处理
if position < 0 or position >= self.len:
print("删除位置无效!")
return -1
# 2.对删除位置进行分析,分为两种情况
# 如果删除的是链表最后一个节点
if position == self.len - 1:
# 将尾指针向前移动一个位置
self.tail = self.tail.prior
self.tail.next = None
else:
# 找到要删除的节点的前一个节点和后一个节点
if position < self.len // 2: # 如果位置在前半部分,从头向后遍历
current = self.head
for _ in range(position + 1):
current = current.next
else: # 如果位置在后半部分,从尾向前遍历
current = self.tail
for _ in range(self.len - position - 1):
current = current.prior
# 断开链接并进行删除操作(在Python中,这会导致被删除节点被垃圾回收)
current.prior.next = current.next
current.next.prior = current.prior
# 双向链表的长度减1
self.len -= 1
return 0
# 判断双向链表是否为空
def is_empty(self):
return self.len == 0
# 求双向链表的长度
def length(self):
return self.len
# 测试代码
if __name__ == "__main__":
dll = DoubleLinkedList()
dll.insert(0, 10) # 在位置0插入数据10
dll.insert(1, 20) # 在位置1插入数据20
dll.insert(1, 15) # 在位置1插入数据15(应该在20之前)
dll.insert(3, 30) # 在位置3插入数据30
# 打印链表内容(从头节点后的第一个节点开始,直到尾节点前的最后一个节点)
current = dll.head.next
while current != dll.tail.next:
print(current.data, end=" -> ")
current = current.next
print("None") # 用None表示链表末尾
(2)双向循环链表
思想和单向循环一样,只需要将双向链表尾的
next
和头的prior
双向链接
解决约瑟夫问题
整体代码:
class Node:
def __init__(self, data):
self.data = data # 节点数据
self.prior = None # 指向前一个节点的指针
self.next = None # 指向下一个节点的指针
class DoubleLinkedList:
def __init__(self):
self.head = None # 链表头指针
self.tail = None # 链表尾指针
def append(self, data):
# 在链表末尾添加新节点
new_node = Node(data)
if not self.head:
# 如果链表为空,则新节点既是头节点也是尾节点
self.head = self.tail = new_node
else:
# 否则,将新节点添加到链表末尾
self.tail.next = new_node
new_node.prior = self.tail
self.tail = new_node
def make_circular(self):
# 使链表形成循环
if self.head and self.tail:
self.tail.next = self.head
self.head.prior = self.tail
def josephus_problem(self, all_num, start_num, kill_num):
# 解决约瑟夫问题
# 填充循环双向链表
for i in range(1, all_num + 1):
self.append(i)
self.make_circular()
# 移动到起始位置
current = self.head
for _ in range(start_num - 1):
current = current.next
# 解决约瑟夫问题
while current.next != current: # 当链表中不止一个节点时
# 移动到要删除的节点
for _ in range(kill_num - 1):
current = current.next
# 删除当前节点
print(f"杀死的是 ------- {current.data}")
if current.prior:
current.prior.next = current.next
if current.next:
current.next.prior = current.prior
# 移动到删除节点后的下一个节点
current = current.next
# 打印最后剩下的节点(猴王)
print(f"猴王是 {current.data}")
# 主函数
if __name__ == "__main__":
dll = DoubleLinkedList() # 创建双向链表实例
all_num = int(input("请您输入猴子的总数: ")) # 输入猴子总数
start_num = int(input("从几号猴子开始数: ")) # 输入开始数数的猴子号码
kill_num = int(input("数到几杀死猴子: ")) # 输入数到几杀死猴子的号码
dll.josephus_problem(all_num, start_num, kill_num) # 解决约瑟夫问题
3.排序算法
(1)冒泡排序
(1)比较第一个数与第二个数,若为逆序a[0]>a[1],则交换;然后比较第二个数与第三个数;依次类推,直至第n-1个数和第n个数比较为止——第一趟冒泡排序,结果最大的数被安置在最后一个元素位置上
(2)对前n-1个数进行第二趟冒泡排序,结果使次大的数被安置在第n-1个元素位置
(3)重复上述过程,共经过n-1趟冒泡排序后,排序结束
def bubble_sort(arr):
N = len(arr)
for i in range(N - 1):
for j in range(N - 1 - i):
if arr[j] > arr[j + 1]:
# 交换元素
temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
def main():
N = 7
a = [0] * N # 初始化数组为0
print("Please input array (int a[7]): ")
# 从标准输入读取7个整数
a = list(map(int, input().split()))[:N] # 确保只获取前7个输入
# 调用冒泡排序函数
bubble_sort(a)
# 打印排序后的数组
for i in range(N):
print(f"{a[i]:\t}", end="")
print() # 换行
if __name__ == "__main__":
main()
C语言:
#include<stdio.h>
#define N 10
int main(int argc, const char *argv[])
{
int st[N],i,j,temp;
for(i = 0; i < N; i++)
{
scanf("%d",&st[i]);
}
for(i = 0; i < N - 1; i++)
{
for(j = 0; j < N - 1 - i; j++)
{
if(st[j] > st[j + 1])
{
temp = st[j];
st[j] = st[j + 1];
st[j + 1] = temp;
}
}
}
for(i = 0; i < N; i++)
{
printf("%d\n",st[i]);
}
return 0;
}
(2)选择排序
(1)首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数交换—第一趟选择排序,结果最小的数被安置在第一个元素位置上
(2)再通过n-2次比较,从剩余的n-1个数中找出次小的记录,将它与第二个数交换—第二趟选择排序
(3)重复上述过程,共经过n-1趟排序后,排序结束
def exchange(arr, i, k):
# 交换数组arr中索引为i和k的元素
arr[i], arr[k] = arr[k], arr[i]
def selection_sort(arr):
N = len(arr)
for i in range(N - 1):
# 假设当前元素i是最小的
k = i
# 在剩余未排序部分中寻找最小值
for j in range(i + 1, N):
if arr[j] < arr[k]:
k = j
# 如果找到的最小值不是当前元素i,则交换它们
if i != k:
exchange(arr, i, k)
def main():
# 初始化一个长度为7的数组,这里使用随机数填充
import random
a = [random.randint(0, 100) for _ in range(7)]
print("原始数组:")
print(a)
# 调用选择排序函数
selection_sort(a)
print("排序后的数组:")
print(a)
if __name__ == "__main__":
main()
C语言:
#include<stdio.h>
#define N 5
int main(int argc, const char *argv[])
{
int st[N] = {0};
int i,j,temp,k;
for(i = 0; i < N; i++)
{
scanf("%d",&st[i]);
}
for(i = 0; i < N - 1; i++)
{
k = i;//开始的时候假设最小的元素的下标为i,对第一趟,开始假设的最小元素为第一个元素.
for(j = i+1; j < N; j++)
{
if(st[k] > st[j])//从一组数据里面找最小的,方法:先假设一个最小的
k = j;
}
if(k != i)
{
temp = st[i];
st[i] = st[k];
st[k] = temp;
}
}
for(i = 0; i < N; i++)
{
printf("%d\n",st[i]);
}
return 0;
}
(3)插值排序
def interpolation_search(arr, target):
low = 0
high = len(arr) - 1
while low <= high and arr[low] <= target <= arr[high]:
# 计算插值位置(核心公式)
# 类似于根据比例估算位置
pos = low + ((target - arr[low]) * (high - low)) // (arr[high] - arr[low])
# 检查找到的位置
if arr[pos] == target:
return pos
# 如果目标值更大,在右半部分查找
elif arr[pos] < target:
low = pos + 1
# 如果目标值更小,在左半部分查找
else:
high = pos - 1
# 特殊情况:检查low位置是否为目标(当数组中只有一个元素时)
if low <= high and arr[low] == target:
return low
# 未找到目标
return -1
# 测试
if __name__ == "__main__":
# 插值查找要求数组是有序的
arr = [10, 12, 13, 16, 18, 19, 20, 21, 22, 23, 24, 33, 35, 42, 47]
target = 18
result = interpolation_search(arr, target)
if result != -1:
print(f"元素 {target} 找到,索引位置为 {result}")
else:
print(f"元素 {target} 未在数组中找到")
(4)快速排序
其实就是逐个找到单个数据的位置
def get_pivot(arr, low, high):
pivot = arr[low] # 选择第一个元素作为枢轴
left = low
right = high
while left <= right:
# 从右向左扫描
while arr[right] >= pivot and left <= right:
right -= 1
if left <= right:
arr[left] = arr[right] # 将小于枢轴的元素移动到左边
left += 1
# 从左向右扫描
while arr[left] <= pivot and left <= right:
left += 1
if left <= right:
arr[right] = arr[left] # 将大于枢轴的元素移动到右边
right -= 1
# 将枢轴放置到正确的位置
arr[left] = pivot
return left # 返回枢轴的位置
def show_array(arr):
print(" ".join(map(str, arr)))
print()
def quick_sort(arr, low, high):
if low < high:
pivot_index = get_pivot(arr, low, high)
quick_sort(arr, low, pivot_index - 1) # 递归排序枢轴左侧
quick_sort(arr, pivot_index + 1, high) # 递归排序枢轴右侧
def main():
# 初始化数组
arr = [32, 2, 54, 6, 78, 23, 17, 76]
print("快速排序之前:")
show_array(arr)
quick_sort(arr, 0, len(arr) - 1)
print("快速排序之后:")
show_array(arr)
if __name__ == "__main__":
main()