飞道的博客

python之数据结构与算法分析

317人阅读  评论(0)

https://blog.csdn.net/weixin_45912307/article/details/115792813

01 数据结构与算法入门

1.1顺序表

根据线性表的实际存储方式,分为两种实现模型:

  • 顺序表,将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示。
  • 链表,将元素存放在通过链接构造起来的一系列存储块中。

1.2 链表

1.2.1 单向链表

1. 单向链表(单链表) : 它的每个节点包含两个域,一个信息域(元素域)和一个连接域,这个链接指向链表中的下一个节点,而最后一个节点的链接则指向一个空值。

  • 表元素elem用来存放具体数据
  • 链接域next用来存放下一个节点的位置(python中的标识)
  • 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点

2. 单链表的操作

  • is_empty() 链表是否为空
  • length() 链表长度
  • travel() 遍历整个链表
  • add(item) 链表头部添加元素
  • append(item) 链表尾部添加元素
  • insert(pos,item) 指定位置添加元素
  • remove(item) 删除元素
  • search(item) 查找节点是否存在

3. python实现单向链表

class Node(object):
    def __init__(self, elem):
        """
        :param elem: 表元素域
        next:下一结点链接域
        cursor(cur):游标
        """
        self.elem = elem
        # 定义next指向空
        self.next = None


class SingleLinkList(object):
    """
    单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一一个节点,而最后-个节点的链接域则指向一个空值。
    表元素域elem用来存放具体的数据。
    链接域next用来存放下一个节点的位置(python中的标识)
    变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
    """

    def __init__(self, node=None):
        self.__head = node  # node.elem node.next

    def is_empty(self):
        """链表是否为空   """
        return self.__head is None

    def length(self):
        """链表长度"""
        # cur游标,用来移动遍历节点
        cur = self.__head
        count = 0
        while cur is not None:
            count += 1
            cur = cur.next
            # count 记录数量
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=' ')
            cur = cur.next

    def add(self, item):
        """链表头部添加元素:头插法"""
        node = Node(item)
        node.next = self.__head
        self.__head = node

    def append(self, item):
        """链表尾部添加元素:尾插法"""
        node = Node(item)
        # 下一结点链接域不为空
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            cur.next = node

    def insert(self, pos, item):
        """
        pos: pos从0开始
        pre:指定节点前一节点,相当于游标
        node:插入的指定节点
        指定位置添加元素
        """
        # if pos<=0 头插法
        if pos <= 0:
            self.add(item)
        # elif pos>(self.length()-1) 尾插法
        elif pos > (self.length() - 1):
            self.append(item)
        # else 插入法
        else:
            pre = self.__head
            count = 0
            # 当循环退出后,pre指向pos-1
            while count < (pos - 1):
                count += 1
                pre = pre.next
            node = Node(item)
            node.next = pre.next
            pre.next = node

    def remove(self, item):
        """删除元素"""
        # 考虑删除头部、尾部、中间节点
        cur = self.__head
        pre = None
        while cur is not None:
            if cur.elem == item:
                # 先判断是否是头节点
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        # 1. 创建游标
        cur = self.__head
        # 2. 遍历游标
        while cur is not None:
            # 3. cur.elem = item
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False
if __name__ == '__main__':
    ll = SingleLinkList()
    ll.is_empty()
    l1 = ll.length()
    print(l1)

    ll.append(55)
    ll.is_empty()
    l2 = ll.length()
    print(l2)

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    # 55 1 8 2 3 4
    ll.insert(-1, 9)  # 9 8 55 2 1 8 2345
    ll.insert(2, 100)  # 9 8 100 55 2 1 8 2345
    ll.travel()

1.2.2 双向链表

1. 双向链表定义: 一种更复杂的链表是“双向链表"或“双面链表"。每个节点有两个链接: 一个指向前一个节点,当此节点为第
一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
2.双向链表操作:

  • is_ empty()链表是否为空
  • length(链表长度
  • travel)遍历链表
  • add(item)链表头部添加
  • append(item)链表尾部添加
  • insert(pos, item)指定位置添加
  • remove(item)删除节点
  • search(item)查找节点是否存在

3.python实现双向循环链表

class Node(object):
    def __init__(self, elem):
        """
        :param elem: 表元素域
        next:下一结点链接域
        cursor(cur):游标
        """
        self.elem = elem
        # 定义next指向空
        self.next = None
        # 定义next指向空
        self.prev = None


class DoubleLinkList(object):
    """
    一种更复杂的链表是“双向链表"或“双面链表"。每个节点有两个链接: 一个指向前一个节点,当此节点为第
    一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
    """

    def __init__(self, node=None):
        self._head = node  # node.elem node.next

    def is_empty(self):
        """链表是否为空   """
        return self._head is None

    def length(self):
        """链表长度"""
        # cur游标,用来移动遍历节点
        cur = self._head
        count = 0
        while cur is not None:
            count += 1
            cur = cur.next
            # count 记录数量
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self._head
        while cur is not None:
            print(cur.elem, end=' ')
            cur = cur.next

    def add(self, item):
        """链表头部添加元素:头插法"""
        node = Node(item)
        # node的next指向_head
        node.next = self._head
        # _head指向新节点
        self._head = node
        node.next.prev = node

    def append(self, item):
        """链表尾部添加元素:尾插法"""
        node = Node(item)
        # 下一结点链接域不为空
        if self.is_empty():
            self._head = node
        else:
            cur = self._head
            while cur.next is not None:
                cur = cur.next
            cur.next = node
            node.prev = cur

    def insert(self, pos, item):
        """
        pos: pos从0开始
        pre:指定节点前一节点,相当于游标
        node:插入的指定节点
        指定位置添加元素
        """
        # if pos<=0 头插法
        if pos <= 0:
            self.add(item)
        # elif pos>(self.length()-1) 尾插法
        elif pos > (self.length() - 1):
            self.append(item)
        # else 插入法
        else:
            cur = self._head
            count = 0
            # 当循环退出后,cur指向pos
            while count < pos:
                count += 1
                cur = cur.next
            # 当循环退出后,cur指向pos位置
            node = Node(item)
            # 方式1:
            node.next = cur
            node.prev = cur.prev
            cur.prev.next = node
            cur.prev = node
            # 方式2:
            # node.next = cur
            # node.prev = cur.prev
            # cur.prev = node
            # node.prev.next = node

    def remove(self, item):
        """删除元素"""
        # 考虑删除头部、尾部、中间节点
        cur = self._head
        while cur is not None:
            if cur.elem == item:
                # 先判断是否是头节点
                if cur == self._head:
                    self._head = cur.next
                    if cur.next:  # 判断链表表是否只有一个节点
                        cur.next.prev = None
                else:
                    cur.prev.next = cur.next
                    if cur.next:  # 判断链表是否是最后一个节点
                        cur.next.prev = cur.prev
                break
            else:
                cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        # 1. 创建游标
        cur = self._head
        # 2. 遍历游标
        while cur is not None:
            # 3. cur.elem = item
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False


if __name__ == '__main__':
    DLL = DoubleLinkList()
    DLL.is_empty()
    l1 = DLL.length()
    print(l1)

    DLL.append(55)
    DLL.is_empty()
    l2 = DLL.length()
    print(l2)

    DLL.append(2)
    DLL.add(8)
    DLL.append(3)
    DLL.append(4)
    DLL.append(5)
    # 55 1 8 2 3 4
    DLL.insert(-1, 9)  # 9 8 55 2 1 8 2345
    DLL.insert(2, 100)  # 9 8 100 55 2 1 8 2345
    DLL.travel()

1.2.3 单向循环链表

1. 定义: 单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为none,而是指向链表的头节点
2. 单向循环链表操作:

  • is_empty()判断链表是否为空
  • length0返回链表的长度
  • travel()遍历
  • add(item)在头部添加一个节点
  • append(item)在尾部添加一个节点
  • insert(pos, item) 在指定位置pos添加节点
  • remove(item)删除一个节点
  • search(item)查找节点是否存在

3.python实现单向循环链表

class Node(object):
    def __init__(self, elem):
        """
        :param elem: 表元素域
        next:下一结点链接域
        cursor(cur):游标
        """
        self.elem = elem
        # 定义next指向空
        self.next = None


class SingleCircularLinkList(object):
    """
    单向循环链表:单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为none,而是指向链表的头节点
    """

    def __init__(self, node=None):
        self.__head = node  # node.elem node.next
        if node:
            node.next = node

    def is_empty(self):
        """链表是否为空   """
        return self.__head is None

    def length(self):
        """链表长度"""
        if self.is_empty():
            return 0
        # cur游标,用来移动遍历节点
        cur = self.__head
        count = 1
        while cur.next != self.__head:
            count += 1
            cur = cur.next
            # count 记录数量
        return count

    def travel(self):
        """遍历整个链表"""
        if self.is_empty():
            return
        cur = self.__head
        while cur.next != self.__head:
            print(cur.elem, end=' ')
            cur = cur.next
        # 退出循环,cur指向尾结点,但尾节点的元素未打印
        print(cur.elem)

    def add(self, item):
        """链表头部添加元素:头插法"""
        node = Node(item)
        if self.is_empty():
            self.__head = node
            node.next = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            # 退出循环,cur指向尾结点
            node.next = self.__head
            self.__head = node
            # 方式1:cur.next = node
            cur.next = self.__head  # 方式2

    def append(self, item):
        """链表尾部添加元素:尾插法"""
        node = Node(item)
        # 下一结点链接域不为空
        if self.is_empty():
            self.__head = node
            node.next = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            # 方式1:
            # node.next = cur.next
            # cur.next = node
            # 方式2:
            cur.next = node
            node.next = self.__head

    def insert(self, pos, item):
        """
        pos: pos从0开始
        pre:指定节点前一节点,相当于游标
        node:插入的指定节点
        指定位置添加元素
        """
        # if pos<=0 头插法
        if pos <= 0:
            self.add(item)
        # elif pos>(self.length()-1) 尾插法
        elif pos > (self.length() - 1):
            self.append(item)
        # else 插入法
        else:
            pre = self.__head
            count = 0
            # 当循环退出后,pre指向pos-1
            while count < (pos - 1):
                count += 1
                pre = pre.next
            node = Node(item)
            node.next = pre.next
            pre.next = node

    def remove(self, item):
        """删除元素"""
        # 考虑删除头部、尾部、中间节点
        if self.is_empty():
            return
        cur = self.__head
        pre = None
        while cur.next != self.__head:
            if cur.elem == item:
                # 先判断是否是头节点
                if cur == self.__head:
                    # 找到尾节点
                    rear = self.__head
                    while rear.next != self.__head:
                        rear = rear.next
                    self.__head = cur.next
                    rear.next = self.__head
                else:
                    # 中间节点
                    pre.next = cur.next
                return
            else:
                pre = cur
                cur = cur.next
        # 退出循环,cur指向尾结点
        if cur.elem == item:
            if cur == self.__head:
                # 链表只有一个节点
                self.__head = None
            else:
                pre.next = cur.next
    def search(self, item):
        """查找节点是否存在"""
        if self.is_empty():
            return False
        # 1. 创建游标
        cur = self.__head
        # 2. 遍历游标
        while cur.next != self.__head:
            # 3. cur.elem = item
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        # 对于最后一个元素或只有一个元素
        if cur.elem == item:
            return True
        return False


if __name__ == '__main__':
    ll = SingleCircularLinkList()
    ll.is_empty()
    l1 = ll.length()
    print(l1)

    ll.append(55)
    ll.is_empty()
    l2 = ll.length()
    print(l2)

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    # 55 1 8 2 3 4
    ll.insert(-1,9) #  9 8 55 2 1 8 2345
    ll.insert(2,100) #9 8 100 55 2 1 8 2345
    ll.travel()

02 栈与队列

2.1 栈

1.栈的定义(LIFO:last in First Out): 栈是一种操作受限制的线性表,将允许进行插入、删除、访问元素的一端称为栈顶,另一端称为栈底。
2.栈支持的操作:

  • Stack():创建一个空栈。它不需要参数,且会返回一个空栈;
  • s.isEmpty() 检查栈是否为空,它不需要参数,且会返回一个布尔值;
  • s.push(item):将一个元素添加到栈的顶端。它需要一个参数item,且无返回值;
  • s .peek() 返回栈顶端的元素,但是并不移除该元素。它不需要参数,也不会修改栈的内容;
  • s.size() 返回栈中元素的数目。它不需要参数,且会返回一个整数;
  • s.pop():将栈顶端的元素移除。它不需要参数,但会返回顶端的元素并且修改栈的内容;

3.python实现堆栈

class Stack():
    """
    栈方式1:假设列表的尾部是栈的顶端。当栈增长时(即进行push操作), 新的元素会被添加到列表的尾部。pop操作同样会修改这一端。
    将允许进行插入、删除的一端称为栈顶,另一端称为栈底。
    """

    # 创建一个空栈
    def __init__(self):
        self.__list = []

    # 检查栈是否为空:判断是否等于一个空列表
    def isEmpty(self):
        return self.__list == []
        # return not self.__list

    # 统计栈的长度
    def size(self):
        return len(self.__list)

    # 返回栈顶元素
    def peek(self):
        # return self.__list[len(self.__list)-1]
        if self.__list:
            return self.__list[-1]
        else:
            return None

    # 入栈(把列表尾部假设为栈顶)
    def push(self, item):
        self.__list.append(item)

    # 出栈
    def pop(self):
        return self.__list.pop()


class Stack2:
    """
    栈方式2:选择将列表头部作为顶端,必须用pop方法和insert方法显式地访问下标为0的元素,即列表中的第一个元素;
    """

    def __init__(self):
        self.__list = []

    def isEmpty(self):
        """判断栈是否为空"""
        # return self.__list == []
        return not self.__list

    def size(self):
        """返回栈的元素个数"""
        return len(self.__list)

    # 入栈
    def push(self, item):
        """添加一个新的元素item到栈顶"""
        self.__list.insert(0, item)

    # 出栈
    def pop(self):
        return self.__list.pop(0)

    def peek(self):
        """返回栈顶元素"""
        if self.__list:
            return self.__list[0]
        else:
            return None


if __name__ == '__main__':
    s = Stack2()
    print(s.isEmpty())
    s.push('a')
    s.push('b')
    s.push('小明')
    s.push('c')
    print(s.size())

    print(s.pop())
    print(s.pop())
    print(s.pop())
    print(s.pop())

2.2 队列

1. 队列定义(FIFO:First In First Out): 队列是一种特殊的线性表,只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头;
2. 队列支持的操作:

  • Queue()创建一个空队列。它不需要参数,且会返回一个空队列。
  • enqueue(item)在队列的尾部添加一个元素。它需要一个 元素作为参数,不返回任何值。
  • dequeue()从队列的头部移除一个元素。它不需要参数,且会返回一个元素,并修改队列的内容。
  • isEmpty()检查队列是否为空。它不需要参数,且会返回一个布尔值。
  • size()返回队列中元素的数目。它不需要参数,且会返回一个整数。
  • dequeue()从队列的头部移除一个元素。 它不需要参数,且会返回一个元素,并修改队 列的内容。

3.python实现队列:

class Queue1:
    """
    队列方式1:假设列表头部为队头:append向队列尾部添加元素,pop(0)移除队列头部元素
    """

    def __init__(self):
        self.__list = []

    def isEmpty(self):
        return self.__list == []

    def size(self):
        return len(self.__list)

    # 队尾插入元素
    def enqueue(self, item):
        self.__list.append(item)

    # 队头删除元素
    def dequeue(self):
        return self.__list.pop(0)


class Queue2:
    """
    假设列表头部(位置0)为队尾:insert向队列尾部添加元素O(n),pop移除队列头部元素O(1)
    """

    def __init__(self):
        self.__list = []

    def isEmpty(self):
        return self.__list == []

    def size(self):
        return len(self.__list)

    # 队列尾部插入元素
    def enqueue(self, item):
        self.__list.insert(0, item)

    # 把队头删除的元素返回
    def dequeue(self):
        return self.__list.pop()


if __name__ == '__main__':
    q = Queue2()
    print(q.isEmpty())

    q.enqueue(10)
    q.enqueue(100)
    q.enqueue(1000)
    q.enqueue(10000)
    print(q.size())

    print(q.dequeue())  # 10
    print(q.dequeue())  # 100
    print(q.dequeue())  # 1000
    print(q.dequeue())  # 10000
    print(q.size())

2.3 双端队列

1. 双端队列定义: 具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列可以在队列任意一端入队和出队。

2. 双端队列原理图:

3.双端队列支持的操作:

  • Deque()创建一个空的双端队列。它不需要参数,且会返回一个空的双端队列。
  • addFront(item)将一个元素添加到双端队列的前端。它接受一个元素作为参数,没有返回值
  • addRear(item)将一个元素添加到双端队列的后端。它接受一个元素作为参数,没有返回值。
  • removeFront()从双端队列的前端移除一个元素。它不需要参数,且会返回一个元素,并修改双端队列的内容。
  • removeRear()从双端队列的后端移除一个元素。它不需要参数,且会返回一个元素,并修改双端队列的内容。
  • isEmpty()检查双端队列是否为空。它不需要参数,且会返回一个布尔值。
  • size()返回双端队列中元素的数目。它不需要参数,且会返回一个整数。

4.python实现双端队列:

# -- encoding: utf-8 --
# @time:    	2021/4/18 11:49
# @Author: 		jsonLiu
# @Email:  		xxxxxxxxx@qq.com
# @file: 		deque
# 双端队列
"""
- Deque()创建一个空的双端队列。它不需要参数,且会返回一个空的双端队列。
- addFront(item)将一个元素添加到双端队列的前端。它接受一个元素作为参数,没有返回值
- addRear (item)将一个元素添加到双端队列的后端。它接受一个元素作为参数,没有返回值。
- popFront()从双端队列的前端移除一个元素。它不需要参数,且会返回一个元素,并修改双端队列的内容。
- popRear()从双端队列的后端移除一个元素。它不需要参数,且会返回一个元素,并修改双端队列的内容。
- isEmpty()检查双端队列是否为空。它不需要参数,且会返回一个布尔值。
- size()返回双端队列中元素的数目。它不需要参数,且会返回一个整数。
"""


class Deque1:
    """假设双端队列的后端是列表位置0处"""

    def __init__(self):
        self.__list = []

    def isEmpty(self):
        # return self.__list == []
        return not self.__list

    def size(self):
        return len(self.__list)

    def add_front(self, item):
        """添加到双端队列前端"""
        self.__list.append(item)

    def add_rear(self, item):
        """添加到双端队列后端"""
        self.__list.insert(0, item)

    def pop_front(self):
        """从双端队列的前端移除一个元素"""
        return self.__list.pop(len(self.__list) - 1)
        # return self.__list.pop()

    def pop_rear(self):
        """双端队列的后端移除一个元素"""
        return self.__list.pop(0)


class Deque2:
    """假设双端队列的前端是列表位置0处"""

    def __init__(self):
        self.__list = []

    def isEmpty(self):
        # return self.__list == []
        return not self.__list

    def size(self):
        return len(self.__list)

    def add_front(self, item):
        """添加到双端队列前端"""
        self.__list.insert(0, item)

    def add_rear(self, item):
        """添加到双端队列后端"""
        self.__list.append(item)

    def pop_front(self):
        """从双端队列的前端移除一个元素"""
        return self.__list.pop(0)

    def pop_rear(self):
        """双端队列的后端移除一个元素"""
        # return self.__list.pop()
        return self.__list.pop(len(self.__list) - 1)


if __name__ == '__main__':
    d = Deque2()
    # d = Deque1()
    print(d.isEmpty())  # True
    d.add_rear(4)
    d.add_rear(3)
    d.add_front(2)
    d.add_front(1)
    print(d.size())  # 4

    d.add_rear(5)
    print(d.pop_rear())
    print(d.pop_front())

03 排序与搜索

3.1 排序

1.冒泡排序:

  • 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成

2.选择排序

  • 第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序序列的末尾。直到所有元素均排序完毕

3.插入排序:

  • 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描, 找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

4.快速排序

  • 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

5.希尔排序 (插入排序改进版)

  • 先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d,对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成

6.归并排序

  • 先递归分解数组,再合并数组。基本思路是比较两个数组的最前面的数, 谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
python实现排序
# -- encoding: utf-8 --
# @time:    	2021/4/18 12:30
# @Author: 		jsonLiu
# @Email:  		xxxxxxxxx@qq.com
# @file: 		sort_algorithm
# 1. 冒泡排序 最坏时间复杂度n^2,最优时间复杂度O(n)
def bubble_sort(alist):
    if isinstance(alist, tuple):
        alist = list(alist)
    for i in range(len(alist) - 1):  # len(alist)-1比较的轮数
        count = 0
        for j in range(len(alist) - 1 - i):  # len(alist)-1-i每轮需要比较的次数
            if alist[j] > alist[j + 1]:
                alist[j + 1], alist[j] = alist[j], alist[j + 1]
                count += 1
        if 0 == count:  # 当为有序序列时,时间复杂度为O(n)
            return


# 2. 选择排序 最优时间复杂度/最坏时间复杂度 n^2
def selection_sort(alist):
    # 假设位置0为最小值
    n = len(alist)
    for j in range(n - 1):  # j:0~n-2
        min_index = j
        for i in range(j + 1, n):
            if alist[i] < alist[min_index]:
                min_index = i
        alist[j], alist[min_index] = alist[min_index], alist[j]


# 3. 插入排序:最坏时间复杂度O(n^2) 最优时间复杂度O(n)
def insert_sort(alist):
    n = len(alist)
    # 外层循环:从右边的无序序列中取出多少个元素执行这样的过程
    for j in range(1, n):
        i = j
        # 内层循环:执行从右边的无序序列中取出第一个元素,即i位置的元素,然后将其插入到前面的正确位置
        while i > 0:
            if alist[i - 1] > alist[i]:
                alist[i], alist[i - 1] = alist[i - 1], alist[i]
                i -= 1
            else:
                break


# 4. 快速排序 最优时间复杂度nlogn 最坏时间复杂度n^2
def quick_sort(alist, first, last):
    if first >= last:
        return
    mid_value = alist(first)
    low = first
    high = last
    while low < high:
        while low < high and alist[high] >= mid_value:
            high -= 1
        alist[low] = alist[high]
        while low < high and alist[low] < mid_value:
            low += 1
        alist[high] = alist[low]
    # 从顺序退出时,low == high
    alist[low] = mid_value
    # 对low左边列表执行快速排序
    quick_sort(alist, first, low - 1)
    # 对low右边列表排序
    quick_sort(alist, low + 1, last)


# 5.希尔排序
def shell_sort(alist):
    n = len(alist)
    gap = n // 2
    while gap > 0:
        # 与普通插入算法区别就是gap步长
        for j in range(gap, n):
            i = j
            while i > 0:
                if alist[i] < alist[i - gap]:
                    alist[i], alist[i - gap] = alist[i - gap], alist[i]
                    i -= gap
                else:
                    break
        gap //= 2  # gap除以2,缩短步长


# 6.归并算法
def merge_sort(alist):
    n = len(alist)
    if n <= 1:
        return alist
    mid = n // 2
    # left 采用归并排序后形成的有序的新的列表
    left_li = merge_sort(alist[:mid])
    # right 采用归并排序后形成的有序的新的列表
    right_li = merge_sort(alist[mid:])
    # 将两个有序的子序列合并为一个新的整体
    # merge_sort(left,right)
    left_pointer, right_pointer = 0, 0
    new_l = []
    while left_pointer < len(left_li) and right_pointer < len(right_li):
        if left_li[left_pointer] < right_li[right_pointer]:
            new_l.append(left_li[left_pointer])
            left_pointer += 1
        else:
            new_l.append(right_li[right_pointer])
            right_pointer += 1
    # 反之左边或右边列表最后一个值没有append
    new_l += left_li[left_pointer:]
    new_l += right_li[right_pointer:]
    return new_l


if __name__ == '__main__':
    # s = 8,5,10,9,2
    # l = bubble_sort(s)
    # print(l)

    # s = [17,20,93,54,77,31,44]
    # l = selection_sort(s)
    # print(l)

    # s = [20, 17, 93, 54, 77, 31, 44]
    # insert_sort(s)
    # print(s)

    # s = [20, 17, 93, 54, 77, 31, 44, 54]
    # insert_sort(s)
    # print(s)

    # s = [20, 17, 93, 54, 77, 31, 44, 54]
    # shell_sort(s)
    # print(s)

    l = [20, 17, 93, 54, 77, 31, 44, 54]
    print(l)
    new_l = merge_sort(l)
    print(new_l)

3.2 搜索

3.2.1顺序搜索

1.无序列表顺序搜索: 从列表中的第一个元素开始,沿着默认的顺序逐个查看,知道找到目标元素或者查完列表。如果查完列表后仍没有找到目标元素,则说明目标元素不在列表中。
2.有序列表顺序搜索: 假设列表中的元素按升序排列。如果存在目标元素,那么它出现在n个位置中任意一个位置的可能性仍然一样大,因此比较次数与在无序列表中相同。如果不存在目标元素,那么搜索效率就会提高;

def UnsequentialSearch(ulist, item):
    """
    这个函数接受列表与目标元素作为参数, 并返回一个表示目标元素是否存在的布尔值。布尔型变量found的初始值为False, 如果找到目标元素,就将它的值改为Tru
    """
    pos = 0
    found = False
    while pos < len(ulist) and not found:
        if ulist[pos] == item:
            found = True
        else:
            pos += 1
    return found

def OrderedListSequentialSearch(ulist,item):
    pos = 0
    found = False
    stop = False
    while pos < len(ulist) and not found and not stop:
        if ulist[pos] == item:
            found = True
        else:
            if ulist[pos] > item:
                stop = True
            else:
                pos = pos+1
    return found

if __name__ == '__main__':
    # ret = UnsequentialSearch([1, 3, 10, 5, 8], 7)
    # print(ret)
    ret = OrderedListSequentialSearch([1, 3, 5, 7, 10], 6)
    print(ret)

3.2.2.二分查找

1.二分查找定义

  • 二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;
  • 其缺点是要求待查表为有序表,且插入删除困难;
  • 折半查找方法适用于不经常变动而查找频繁的有序列表。
  • 首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;
  • 否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

2.python实现二分查找:

"""
应用前提:在一个含有n个元素的有序序列中定位目标值 时间复杂度:O(logn)
该算法维持两个参数low和high,这样可使所有候选条目的索引位于low和high之间。首先, low=0和high=n-1。然后我们比较目标值和中间值候选项,即索引项[mid]的数据。
mid =L(low + high)/2 ]考虑以下三种情况:
- 如果目标值等于[mid]的数据, 然后找到正在寻找的值,则查找成功并且终止。
- 如果目标值< [mid] 的数据, 对前半部分序列重复这一过程,即索引的范围从low到mid-1.
- 如果目标值> [mid] 的数据,对后半部分序列重复这一过程,即索的范围从mid+1到high。
- 如果low >high,说明索引范围[low, high]为空,则查找不成功。该算法被称为二分查找
"""


def binary_search(alist, item):
    """非递归"""
    first = 0
    last = len(alist) - 1
    found = False
    while first <= last and not found:
        mid = (first + last) // 2
        if alist[mid] == item:
            found = True
        else:
            if item < alist[mid]:
                last = mid - 1
            else:
                first = mid + 1
    return found


def binary_search_recursion(alist, item):
    if len(alist) > 0:
        mid = len(alist) // 2
        if alist[mid] == item:
            return True
        elif item < alist[mid]:
            return binary_search_recursion(alist[:mid], item)
        else:
            return binary_search_recursion(alist[mid + 1:], item)
    return False


if __name__ == '__main__':
    ret = binary_search_recursion([17, 20, 26, 31, 44, 54, 55, 65, 77, 69], 26)
    print(ret)

    ret = binary_search([17, 20, 26, 31, 44, 54, 55, 65, 77, 69], 68)
    print(ret)

3.2.3哈希散列

1.映射支持的操作
  • Map() 创建一个空的映射,他返回一个空的映射集合
  • put(key, val)往映射中加入一个新的键值对。如果键已经存在,就用新值替换旧值。
  • get(key)返回key对应的值。如果key不存在,则返回None。
  • del通过del map[key]这样的语句从映射中删除键-值对。
  • len()返回映射中存储的键-值对的数目。
  • in通过key in map这样的语句,在键存在时返回True,否则返回False。
2. python实现映射
class Map(object):
    def __init__(self,size=11):
        self.size = size
        self.__slots = [None] * self.size
        self.__data = [None] * self.size

    def put(self, key, val):
        hashvalue = self.hashfunction(key, len(self.__slots))
        if self.__slots[hashvalue] == None:
            self.__slots[hashvalue] = key
            self.__data[hashvalue] = val
        else:
            if self.__slots[hashvalue] == key:
                self.__data[hashvalue] = val
            else:
                nextslot = self.rehash(hashvalue, len(self.__slots))
                while self.__slots[nextslot] != None and self.__slots[nextslot] != key:
                    nextslot = self.rehash(nextslot, len(self.__slots))
                if self.__slots[nextslot] == None:
                    self.__slots[nextslot] = key
                    self.__data[nextslot] = val
                else:
                    self.__data[nextslot] = val

    def get(self, key):
        startslot = self.hashfunction(key, len(self.__slots))
        data = None
        stop = False
        found = False
        position = startslot
        while self.__slots[position] != None and \
                not found and not stop:
            if self.__slots[position] == key:
                found = True
                data = self.__data[position]
            else:
                position = self.rehash(position, len(self.__slots))
            if position == startslot:
                stop = True
        return data
    def delete(self,key):
        pass
    def __getitem__(self, key):
        return self.get(key)

    def __setitem__(self, key, val):
        self.put(key, val)
    def __delitem__(self, key):
        self.delete(key)

    def len(self):
        pass

    def hashfunction(self, key, size):
        return key % size

    def rehash(self, oldhash, size):
        return (oldhash + 1) % size

04 树与算法

4.1树的定义

树(tree)是一种抽象数据类型〈ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集食。它是由n (n>=1)个有限节点组成一个具有层次关系的集合。把它叫做"树"是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点∶

  • 每个节点有零个或多个子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树;

4.2 树的术语

  • 节点的度:一个节点含有的子树的个数称为该节点的度;
  • 树的度:一棵树中,最大的节点的度称为树的度;
  • 叶节点或终端节点︰度为零的节点;
  • 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 树的高度或深度︰树中节点的最大层次;
  • 堂兄弟节点︰父节点在同一层的节点互为堂兄弟;
  • 节点的祖先:从根到该节点所经分支上的所有节点;
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  • 森林:由m(m>=O)棵互不相交的树的集合称为森林;

4.3树的种类

  • 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;
  • 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;
  • 二叉树:每个节点最多含有两个子树的树称为二叉树;
    • 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
    • 平衡二叉树(AVL树) :当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
    • 排序二叉树(二叉查找树(英语: Binary Search Tree) ,也称二叉搜索树、有序二叉树) ;
  • 霍夫曼树(用于信息编码) :带权路径最短的二叉树称为哈夫曼树或最优二叉树;
  • B树: 一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

4.4 树的存储方式

  • 顺序存储:将数据结构存储在固定的数组中,然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。
  • 链式存储

4.5 树的常见应用

  • 1.xml,html
  • 2.路由协议
  • 3.mysq|数据库索引
  • 4.文件系统的目录结构
  • 5.decision

4.6 二叉树

4.6.1 二叉树性质

  • 性质1:在二叉树的第i层上至多有2^(i-1)个结点(i>0)
  • 性质2:深度为k的二叉树至多有2^k- 1个结点(k>0)
  • 性质3:对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
  • 性质4:具有n个结点的完全二叉树的深度必为log2(n+1)
  • 性质5:对完全二叉树,若从上至下、从左至右编号,则编号为i的结点,其左孩子编号必为2i,其右孩子编号
    必为2i+1;其双亲的编号必为i/2 (i=1 时为根,除外)

4.6.2 二叉树支持的操作

  • BinaryTree()创建一个二叉树实例。
  • getLeftChild()返回当前节点的左子节点所对应的二叉树。
  • getRightChild()返回当前节点的右子节点所对应的二叉树。
  • setRootval(val)在当前节点中存储参数val中的对象。
  • getRootval()返回当前节点存储的对象。
  • insertLeft(val)新建一棵二叉树,并将其作为当前节点的左子节点。
  • insertRight (val)新建一棵二叉树,并将其作为当前节点的右子节点。

4.6.3 二叉树深度遍历(三种方式)

  • 层次(从上往下从左往右)
  • 先序(先根再左后右):前序遍历在前序遍历中,先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。
  • 中序(左中右):在中序遍历中,先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。
  • 后序(左右中)先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点

4.6.4 python实现二叉树

class Node(object):
    def __init__(self,item):
        self.elem = item
        self.rchild = None
        self.lchild = None

class BinaryTree(object):
    def __init__(self):
        self.root = None
    def add(self,item):
        # 添加元素操作
        node = Node(item)
        if self.root == None:
            self.root = node
            return
        queue = [self.root]
        while queue:
            cur_node = queue.pop(0)
            if cur_node.lchild is None:
                cur_node.lchild = node
                return
            else: # 左子节点存在则append追加
                queue.append(cur_node.lchild)
            if cur_node.rchild is None:
                cur_node.rchild = node
                return
            else:
                queue.append(cur_node.rchild)
    # 广度遍历(层次遍历)
    def breadth_travel(self):
        queue = [self.root]
        if self.root is None:
            return
        while queue:
            cur_node = queue.pop(0)
            print(cur_node.elem,end=" ")
            if cur_node.lchild is not None:
                queue.append(cur_node.lchild)
            if cur_node.rchild is not None:
                queue.append(cur_node.rchild)
    def preorder(self,node):
        """先序遍历"""
        if node is None:
            return
        print(node.elem,end=" ")
        self.preorder(node.lchild)
        self.preorder(node.rchild)
    def inorder(self,node):
        """中序遍历"""
        if node is None:
            return
        self.inorder(node.lchild)
        print(node.elem, end=" ")
        self.inorder(node.rchild)
    def postorder(self,node):
        """后序遍历"""
        if node is None:
            return
        self.postorder(node.lchild)
        self.postorder(node.rchild)
        print(node.elem, end=" ")
if __name__ == '__main__':
    tree = BinaryTree()
    tree.add(1)
    tree.add(3)
    tree.add(5)
    tree.add(7)
    tree.add(9)
    tree.add(11)
    tree.breadth_travel() # 层次:1 3 5 7 9 11
    print("\n")
    tree.preorder(tree.root) # 先序 1 3 7 9 5 11
    print("\n")
    tree.inorder(tree.root) # 中序:7 3 9 1 11 5
    print("\n")
    tree.postorder(tree.root) # 后序:7 9 3 11 5 1

转载:https://blog.csdn.net/weixin_45912307/article/details/115792813
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场