python---数据结构和算法复习

链表和二叉树

# 1、链表反转

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


def nonrecurse(head):  # 循环的方法反转链表
    if head is None or head.next is None:
        return head
    pre = None
    cur = head
    h = head
    while cur:
        h = cur
        tmp = cur.next
        cur.next = pre
        pre = cur
        cur = tmp
    return h


head = ListNode(1)  # 测试代码
p1 = ListNode(2)  # 建立链表1->2->3->4->None;
p2 = ListNode(3)
p3 = ListNode(4)
head.next = p1
p1.next = p2
p2.next = p3
p = nonrecurse(head)  # 输出链表 4->3->2->1->None
while p:
    print(p.val)
    p = p.next



# 2、链表排序
# 快排法
    def sortList2(self, head):
        # write your code here
        if head is None or head.next is None:
            return head
        pivot = head
        p = pivot
        l1 = ListNode(0)
        l2 = ListNode(0)
        s = l1
        f = l2
        tmp = head.next
        while tmp is not None:
            if tmp.val < pivot.val:
                s.next = tmp
                s = s.next
            elif tmp.val == pivot.val:
                p.next = tmp
                p = p.next
            else:
                f.next = tmp
                f = f.next
            tmp = tmp.next
        s.next = None
        f.next = None
        p.next = None
        l3 = self.sortList(l1.next)
        l4 = self.sortList(l2.next)
        if l3 is not None:
            l5 = l3
            while l5.next is not None:
                l5 = l5.next
            l5.next = pivot
            p.next = l4
            return l3
        else:
            p.next = l4
        return pivot

# 3、链表去重
class Solution:
    """
    @param head: A ListNode
    @return: A ListNode
    """
    def deleteDuplicates(self, head):
        if head is None:
            return head
        record = {[head.val]}
        cur, pre = head.next, head
        while cur:
            if cur.val in record:
                pre.next = cur.next
                cur = cur.next
            else:
                record.add(cur.val)
                pre = pre.next
                cur = cur.next
        return head


# 4、链表是否闭环
class Node():  # 定义一个Node类,构造两个属性,一个是item节点值,一个是节点的下一个指向
    def __init__(self, item=None):
        self.item = item
        self.next = None


def findbeginofloop(head):  # 判断是否为环结构并且查找环结构的入口节点
    slowPtr = head  # 将头节点赋予slowPtr
    fastPtr = head  # 将头节点赋予fastPtr
    loopExist = False  # 默认环不存在,为False
    if head == None:  # 如果头节点就是空的,那肯定就不存在环结构
        return False
    while fastPtr.next != None and fastPtr.next.next != None:  # fastPtr的下一个节点和下下个节点都不为空
        slowPtr = slowPtr.next  # slowPtr每次移动一个节点
        fastPtr = fastPtr.next.next  # fastPtr每次移动两个节点
        if slowPtr == fastPtr:  # 当fastPtr和slowPtr的节点相同时,也就是两个指针相遇了
            loopExist = True
            print("存在环结构")
            break

    if loopExist == True:
        slowPtr = head
        while slowPtr != fastPtr:
            fastPtr = fastPtr.next
            slowPtr = slowPtr.next
        return slowPtr

    print("不是环结构")
    return False


if __name__ == "__main__":
    node1 = Node(1)
    node2 = Node(2)
    node3 = Node(3)
    node4 = Node(4)
    node5 = Node(5)
    node1.next = node2
    node2.next = node3
    node3.next = node4
    node4.next = node5
    node5.next = node2
    print(findbeginofloop(node1).item)


# 5、合并两个有序链表
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution:
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        head = ListNode(0)
        first = head
        while l1 != None and l2 != None:
            if l1.val > l2.val:
                head.next = l2
                l2 = l2.next
            else:
                head.next = l1
                l1 = l1.next
            head = head.next
        if l1 == None:
            head.next = l2
        elif l2 == None:
            head.next = l1
        return first.next


# 6、求出链表倒数第k个值
def FindKthToTail(self, head, k):
    # write code here
    res = []  # 用列表来代替栈
    while head:
        res.append(head)
        head = head.next
    if k > len(res) or k < 1:
        return None
    return res[-k]


# 7、删除链表的当前节点
def removeElements(self, head, val):
    """
    :type head: ListNode
    :type val: int
    :rtype: ListNode
    """
    if head:
        while head.val == val:
            head = head.next
            if head is None:
                return head
        q = head
        p = q.next
        while p:
            if p.val == val:
                q.next = p.next
            else:
                q = q.next
            p = p.next
    return head


# 8、链表的中间节点
# 快慢指针,快指针走完,慢指针在中间位置
def midoflist(self, pHead):
    fast, slow = pHead, pHead
    while fast and fast.next:
        # 这个条件很重要,如果设置为fast.next的话会出现Nonetype没有next这个错误。
        # fast 在前,如果fast已经为None,那么,fast.next也一定不存在
        fast = fast.next.next
        slow = slow.next
    print(slow.val)

# 9、在一个有环链表中找到环的入口
# 一个从相交的地方走,一个从头走,当相遇的时候,这个相遇的节点就是入口
    def circlestart(self, pHead):
        fast, slow = pHead, pHead
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if (fast == slow):
                break
        slow = pHead
        while fast != slow:
            fast = fast.next
            slow = slow.next
        print(fast.val)
        return fast


# 二叉树
class Node(object):
    """节点类"""
    def __init__(self, elem=-1, lchild=None, rchild=None):
        self.elem = elem
        self.lchild = lchild
        self.rchild = rchild


class Tree(object):
    """树类"""
    def __init__(self):
        self.root = Node()
        self.myQueue = []

    def add(self, elem):
        """为树添加节点"""
        node = Node(elem)
        if self.root.elem == -1:  # 如果树是空的,则对根节点赋值
            self.root = node
            self.myQueue.append(self.root)
        else:
            treeNode = self.myQueue[0]  # 此结点的子树还没有齐。
            if treeNode.lchild == None:
                treeNode.lchild = node
                self.myQueue.append(treeNode.lchild)
            else:
                treeNode.rchild = node
                self.myQueue.append(treeNode.rchild)
                self.myQueue.pop(0)  # 如果该结点存在右子树,将此结点丢弃。


    def front_digui(self, root):
        """利用递归实现树的先序遍历"""
        if root == None:
            return
        print(root.elem)
        self.front_digui(root.lchild)
        self.front_digui(root.rchild)


    def middle_digui(self, root):
        """利用递归实现树的中序遍历"""
        if root == None:
            return
        self.middle_digui(root.lchild)
        print(root.elem)
        self.middle_digui(root.rchild)


    def later_digui(self, root):
        """利用递归实现树的后序遍历"""
        if root == None:
            return
        self.later_digui(root.lchild)
        self.later_digui(root.rchild)
        print(root.elem)


    def front_stack(self, root):
        """利用堆栈实现树的先序遍历"""
        if root == None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:                     #从根节点开始,一直找它的左子树
                print(node.elem)
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()            #while结束表示当前节点node为空,即前一个节点没有左子树了
            node = node.rchild                  #开始查看它的右子树


    def middle_stack(self, root):
        """利用堆栈实现树的中序遍历"""
        if root == None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:                     #从根节点开始,一直找它的左子树
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()            #while结束表示当前节点node为空,即前一个节点没有左子树了
            print(node.elem)
            node = node.rchild                  #开始查看它的右子树


    def later_stack(self, root):
        """利用堆栈实现树的后序遍历"""
        if root == None:
            return
        myStack1 = []
        myStack2 = []
        node = root
        myStack1.append(node)
        while myStack1:                   #这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
            node = myStack1.pop()
            if node.lchild:
                myStack1.append(node.lchild)
            if node.rchild:
                myStack1.append(node.rchild)
            myStack2.append(node)
        while myStack2:                         #将myStack2中的元素出栈,即为后序遍历次序
            print(myStack2.pop().elem)


    def level_queue(self, root):
        """利用队列实现树的层次遍历"""
        if root == None:
            return
        myQueue = []
        node = root
        myQueue.append(node)
        while myQueue:
            node = myQueue.pop(0)
            print(node.elem)
            if node.lchild != None:
                myQueue.append(node.lchild)
            if node.rchild != None:
                myQueue.append(node.rchild)


if __name__ == '__main__':
    """主函数"""
    elems = range(10)           #生成十个数据作为树节点
    tree = Tree()          #新建一个树对象
    for elem in elems:
        tree.add(elem)           #逐个添加树的节点

    print('队列实现层次遍历:')
    tree.level_queue(tree.root)

    print('\n\n递归实现先序遍历:')
    tree.front_digui(tree.root)
    print('\n递归实现中序遍历:')
    tree.middle_digui(tree.root)
    print('\n递归实现后序遍历:')
    tree.later_digui(tree.root)

    print('\n\n堆栈实现先序遍历:')
    tree.front_stack(tree.root)
    print('\n堆栈实现中序遍历:')
    tree.middle_stack(tree.root)
    print('\n堆栈实现后序遍历:')
    tree.later_stack(tree.root)

时间复杂度 / 空间复杂度:分别用来评估算法运行效率和内存占用大小的式子。

### 递归的两个特点:

1. 调用自身
2. 结束条件

```python
def func(x):
if x > 0:
func(x - 1)
print(x)


func(5)
# 1,2,3,4,5 递归调用func,递归结束后,从内至外print(x)
```

### 斐波那契数列:
```python
a = 0
b = 1
while b < 1000:
print(b)
a, b = b, a + b
```
```python
li = []
for i in range(20):
if i == 0 or i == 1:
li.append(1)
else:
li.append(li[i-1] + li[i-2])

print(li)
```
### 台阶例子:

```python
# 有20级台阶的楼梯,一次可以迈一级或两级台阶,那么爬完此楼梯有几种方法?
li = []
for i in range(20):
if i == 0:
li.append(1) # 一级台阶1种走法
elif i == 1:
li.append(2) # 二级台阶2种走法
else:
li.append(li[i - 2] + li[i - 1]) # 后一级台阶的走法是前两级走法之和
print(li)

# [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946]
```

```python
# 一楼梯共有20级台阶,规定每步可以迈1级台阶或2级台阶或3级台阶,那么爬完此楼梯有几种方法?

li = [] # 1、2、3级台阶分别有1、2、4种走法
for i in range(20):
if i == 0:
li.append(1)
elif i == 1:
li.append(2)
elif i == 2:
li.append(4)
else:
li.append(li[i-3] + li[i-2] + li[i-1]) # 后一级台阶的走法是前三级走法之和

print(li)

# [1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, 927, 1705, 3136, 5768, 10609, 19513, 35890, 66012, 121415]

```
### 二分查找
针对的是有序列表。从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。

```python
def bin_search(data,value):
low = 0
high = len(data) - 1
while low <= high:
mid = (low + high) // 2
if data[mid] == value:
return mid
elif data[mid] > value:
high = mid -1
else:
low = mid + 1
else:
return "查无此值"


li = list(range(1,10))
print(bin_search(li,5))
```
### 递归版二分查找

```python
def bin_search_rec(data, value, low, high):
if low <= high:
mid = (low + high) // 2
if data[mid] == value:
return mid
elif data[mid] > value:
return bin_search_rec(data, value, low, mid - 1)
else:
return bin_search_rec(data, value, mid + 1, high)
else:
return "查无此值"


li = list(range(1, 10))
high = len(li) - 1
print(bin_search_rec(li, 6, 0, high))
```

### 列表排序
将无序列表变为有序列表。

```python
排序low B三人组:
冒泡排序 # 重点掌握
选择排序
插入排序

排序NB三人组:
快速排序 # 重点掌握
堆排序
归并排序

基本无人用的排序(了解):
基数排序
希尔排序
桶排序
```

### 冒泡排序
关键点:趟 | 无序区。

```python
def bubble_sort(li):
# 趟
for i in range(len(li) - 1):
# 无序区
for j in range(len(li) - i - 1):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1] , li[j]


import random

li = list(range(1, 11))
random.shuffle(li)
bubble_sort(li)
print(li)

时间复杂度:O(n2),空间复杂度O(1)
```

冒泡排序--优化。如果冒泡排序中执行一趟而没有交换,则列表已经是有序状态,可以直接结束算法。

```python
def bubble_sort(data):
# 趟
for i in range(len(data) - 1):
exchange = False
# 无序区
for j in range(len(data) - i - 1):
if data[j] > data[j + 1]:
data[j], data[j + 1] = data[j + 1], data[j]
exchange = True
if not exchange:
return


import random

li = list(range(1, 16))
random.shuffle(li)
bubble_sort(li)
print(li)
```

### 快速排序

快排思路:

1. 取一个元素p(第一个元素),使元素p归位;
2. 列表被p分为两部分,左边都比p小,右边都比p大;
3. 递归完成排序。

关键点:整理 | 递归

```python
# 分割函数
def partition(data, left, right):
tem = data[left]
while left < right:
while left < right and data[right] >= tem:
right -= 1
data[left] = data[right]

while left < right and data[left] <= tem:
left += 1
data[right] = data[left]

data[left] = tem
return left


def quick_sort(data, left, right):
if left < right:
mid = partition(data, left, right)
quick_sort(data, left, mid - 1)
quick_sort(data, mid + 1, right)


li = list(range(1, 11))
import random

random.shuffle(li)
quick_sort(li, 0, len(li) - 1)

print(li)

时间复杂度O(nlogn),空间复杂度:平均情况O(logn) | 最坏情况O(n)
```
### 二叉树

二叉树:度不超过2的树(节点最多有两个叉);

满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树;

完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边若干位置的二叉树。

```python
# 二叉树的存储方式:链式存储方式;顺序存储方式(列表)。
父节点和左孩子节点的编号下标的关系:
2i+ 1;

父节点和右孩子节点的编号下标的关系:
2i + 2;

# (完全)二叉树可以利用列表来存储,通过规律可以从父亲找到孩子或从孩子找到父亲。

```

### 堆
大根堆:一颗完全二叉树,满足任一节点都比其孩子节点大;
小根堆:一个完全二叉树,满足任一节点都比其孩子节点小。


# 数据结构

数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。

简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。

程序 = 数据结构 + 算法

数据结构按照其逻辑结构可分为线性结构、树结构、图结构:

1. 线性结构:数据结构中的元素存在一对一的相互关系;
2. 树结构:数据结构中的元素存在一对多的相互关系;
3. 数据结构中的元素存在多对多的相互关系。

### 栈

```
栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。
栈的特点:后进先出(last-in, first-out)
栈的概念:栈顶、栈底
栈的基本操作:进栈(压栈)push 出栈pop 取栈顶gettop
```
### 队列

```
队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。
进行插入的一端称为队尾(rear),插入动作称为进队或入队
进行删除的一端称为队头(front),删除动作称为出队
队列的性质:先进先出(First-in, First-out)
双向队列:队列的两端都允许进行进队和出队操作。

队列的实现原理:环形队列
```

```python
# Python处理队列的模块
from collections import deque
# Python处理堆的模块
import heapq
```
### 链表

链表中每一个元素都是一个对象,每个对象称为一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的相互连接,最终串联成一个链表。

```python
# 节点定义
class Node(object):
def __init__(self,item):
self.item = item
self.next = None
```
双链表中每个节点有两个指针:一个指向后面节点、一个指向前面节点。

```python
class Node(object):
def __init__(self, item=None):
self.item = item
self.next = None
self.prior = None
```

###哈希表
哈希表是一个通过哈希函数来计算数据存储位置的数据结构。通常支持如下操作:
* insert(key, value):插入键值对(key,value)
* get(key):如果存在键为key的键值对则返回其value,否则返回空值
* delete(key):删除键为key的键值对

```
哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。
哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标。
```
由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突。
比如:h(k)=k mod 7, h(0)=h(7)=h(14)=...

解决哈希冲突
开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
字典与集合都是通过哈希表来实现的。

### 二叉树
二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。

**二叉树的遍历**
对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们平常所说的层次遍历。
要点:递归思想,每一级子树都遵循这个规则。

前序遍历:根结点 ---> 左子树 ---> 右子树
中序遍历:左子树---> 根结点 ---> 右子树
后序遍历:左子树 ---> 右子树 ---> 根结点
层次遍历:只需按层次遍历即可

二叉搜索树是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key ≤ x.key;如果y是x右子树的一个节点,那么y.key ≥ x.key.
平均情况下,二叉搜索树进行搜索的时间复杂度为O(nlogn)。

```
AVL树:AVL树是一棵自平衡的二叉搜索树。
B树(B-Tree):B树是一棵自平衡的多路搜索树。常用于数据库的索引。
```

猜你喜欢

转载自www.cnblogs.com/Zq-sagittarius/p/9975908.html