算法和数据结构
程序 = 算法 + 数据结构
在刷题的时候遇到数据结构知识不够,特来补充学习。
前言
开始学习数据结构。
1 抽象数据类型和面向对象编程
什么是抽象数据类型 ADT
实际上 python 内置的 list 就可以看成一种抽象数据类型。 ADT: Abstract Data Type,抽象数据类型,我们在组合已有的数据结构来实现一种新的数据类型, ADT 定义了类型的数据和操作。 我们以抽象一个背包(Bag) 数据类型来说明,背包是一种容器类型,我们可以给它添加东西,也可以移除东西,并且我们想知道背包里 有多少东西。于是我们可以定义一个新的数据类型叫做 Bag.
例如:
class Bag:
""" 背包类型 """
pass
数据结构与算法–ADT http://www.atjiang.com/data-structures-using-python-ADT/
2 数组和列表
2.1 数组 array
推荐你用 numpy.array
2.2 列表 list
1、列表操作 平均时间复杂度 list[index] O(1) list.append O(1) list.insert O(n) list.pop(index), default last element O(1) list.remove O(n)
3 线性查找
3.1 实现线性表顺序存储的插入操作
def insertlist(L, i, item):
n = len(L)
if i < 0 or i > n:
return False
else:
tempt_list = list(range(i - 1, n))
for j in tempt_list[::-1]:
L[j + 1:j + 2] = [L[j]]
L[i - 1] = item
return L
def insert_list(L, i, item):
n = len(L)
if i < 0 or i > n:
return False
else:
L.insert(i, item)
return L
if __name__ == '__main__':
L = [2, 6, 7, 8, 0]
i = 2
item = 99
res = insertlist(L, i, item)
print(res)
Python中list赋值时, L1=L 与 L1=L[:] 有什么区别? 严格的说,python没有赋值,只有名字到对象的绑定。所以L1=L是把L所指的对象绑定到名字L1上,而L2=L[:]则是把L通过切片运算取得的新列表对象绑定到L2上。前者两个名字指向同一个对象,后者两个名字指向不同对象。换句话说,L1和L是指的同一个东西,那么修改L1也就修改了L;L2则是不同的东西,修改L2不会改变L。注意这个引用的概念对于所有的东西都成立,例如容器内部存储的都是引用…… 很容易理解了吧,
L1=L 意思是将L1也指向L的内存地址,
L1=L[:] 意思是, 复制L的内容并指向新的内存地址.
3.2 实现线性表顺序存储的删除操作
def delete_list(L, i):
n = len(L)
if i < 0 or i > n:
return False
else:
for k in range(i-1, n - 1)[::1]:
L[k] = L[k + 1]
L.pop()
return L
if __name__ == '__main__':
L = [2, 6, 7, 8, 1]
i = 1
res = delete_list(L, i)
print(res)
# 4 排序基础 ## 4.1 冒泡排序 冒泡排序(Bubble Sort)也是一种简单直观的排序算法。 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
"""
冒泡算法:多次遍历列表,将不合顺序的进行交换,一轮遍历将最大值放在列表最后面,
"""
def sortBubble(alist):
al=len(alist)-1
while al>0:
for i in range(al):
if alist[i]>alist[i+1]:
alist[i],alist[i+1]= alist[i+1],alist[i]
al-=1
return alist
if __name__ == "__main__":
res= sortBubble([2,5,3,8,2,6,4])
print(res)
针对有的列表有部分数据已经有序,不需要在进行判断,则对冒泡排序进行改进。
def optsortBubble(alist):
al=len(alist)-1
exchange=True
while al>0 and exchange:
exchange=False
for i in range(al):
if alist[i]>alist[i+1]:
exchange = True
alist[i],alist[i+1]= alist[i+1],alist[i]
al-=1
return alist
if __name__ == "__main__":
res= optsortBubble([2,3,5,8,2,6,4])
print(res)
4.2 选择排序
选择排序是在冒泡排序的基础上进行改进。每次遍历列表只进行一次交换。要实现这一点,选择排序每次遍历寻找最大值,并在遍历完成后将它放在正确的位置上。
.算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
"""
选择排序算法:多次遍历列表,一轮遍历将最大值放在列表最后面。并且每次缩小遍历的列表的大小。
"""
def optselectBubble(alist):
al=len(alist)
tempt=0
while al>1:
for i in range(0,al):
if alist[i]>alist[tempt]:
tempt=i
alist[i],alist[tempt]= alist[tempt],alist[i]
print(alist)
al=al-1
tempt=0
return alist
if __name__ == "__main__":
res= optselectBubble([2,5,1,3,8,2,6,4])
print("最终结果:")
print(res)```
通过每次记录最小值:
```python
def select_sort(L):
n=len(L)
if n==0:
return None
for i in range(n-1):
minindex=i
for j in range(i+1,n):
if L[j]<L[minindex]:
minindex=j
L[minindex],L[i]= L[i],L[minindex]
return L
if __name__ == '__main__':
L = [3,2, 6]
res = select_sort(L)
print(res)```
![在这里插入图片描述](https://img-blog.csdnimg.cn/c21f16b150af4e4db274bbb2513b7d41.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAc2VhX2Jp,size_20,color_FFFFFF,t_70,g_se,x_16)
```python
def insertsortBubble(alist):
for index in range(1, len(alist)):
currentvalue = alist[index]
for i in range(index):
if alist[i] > currentvalue:
alist[i], alist[index] = alist[index], alist[i]
return alist
if __name__ == "__main__":
res = insertsortBubble([1, 2, 5, 1, 3, 8, 2, 6, 4])
print(res)
4.2 插入排序
def insertsortBubble(alist):
for index in range(1, len(alist)):
currentvalue = alist[index]
for i in range(index):
if alist[i] > currentvalue:
alist[i], alist[index] = alist[index], alist[i]
return alist
if __name__ == "__main__":
res = insertsortBubble([1, 2, 5, 1, 3, 8, 2, 6, 4])
print(res)
Java实现:
4.3 希尔排序
希尔排序是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 希尔排序的核心是对步长的理解,步长是进行相对比较的两个元素之间的距离,随着步长的减小,相对元素的大小会逐步区分出来并向两端聚拢,当步长为1的时候,就完成最后一次比较,那么序列顺序就出来了。
def shell_sort(items):
"""
希尔排序
:param items:
:return:
"""
n = len(items)
step = n // 3
print("++++1++++")
print("step:" + str(step))
while step > 0:
for cur in range(step, n):
i = cur
while i >= step and items[i-step] > items[i]:
print(i,i-step)
items[i - step], items[i] = items[i], items[i-step]
i -= step
step = step // 3
print("++++2++++")
print("step:"+str(step))
if __name__ == "__main__":
arr = [2, 5, 8, 0, 1, 4, 2, 7, 10, 67, 34]
shell_sort(arr)
n=len(arr)
print("\n排序后:")
for i in range(n):
print(arr[i], end=",")
python代码演示,是从step到n,然后从后面跟前面比较
def shell_sort(items):
"""
希尔排序
:param items:
:return:
"""
n = len(items)
step = n // 3
print("++++1++++")
print("step:" + str(step))
while step > 0:
for cur in range(step, n):
i = cur
print("i:"+str(i))
while i >= step and items[i-step] > items[i]:
print(i,i-step)
items[i - step], items[i] = items[i], items[i-step]
i -= step
step = step // 3
print("++++2++++")
print("step:"+str(step))
if __name__ == "__main__":
arr = [2, 5, 8, 0, 1, 4, 2, 7, 10, 67, 34,66,334,12,213,32,454,67,23,12,345]
shell_sort(arr)
n=len(arr)
print("\n排序后:")
print("n:"+str(n))
for i in range(n):
print(arr[i], end=",")
4.4 快速排序
快速排序采用分治策略。
快速排序是一种交换排序。
基本思想: 通过一趟排序将要排序的数据分割成独立的两部分:分割点左边都是比它小的数,右边都是比它大的数。
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
详细的图解往往比大堆的文字更有说明力,所以直接上图:
public int division(int[] list, int left, int right) {
int base = list[left];
while (left < right) {
while (left < right && list[right] >= base)
right--;
list[left] = list[right];
while (left < right && list[left] <= base)
left++;
list[right] = list[left];
}
list[left] = base;
return left;
}
private void quickSort(int[] list, int left, int right){
if (left < right) {
int base = division(list, left, right);
System.out.format("base = %d:\t", list[base]);
printPart(list, left, right);
quickSort(list, left, base - 1);
quickSort(list, base + 1, right);
}
}
python实现
def partition(arr,low,high):
i = ( low-1 )
pivot = arr[high]
for j in range(low , high):
if arr[j] <= pivot:
i = i+1
arr[i],arr[j] = arr[j],arr[i]
arr[i+1],arr[high] = arr[high],arr[i+1]
return i+1
def quickSort(arr,low,high):
if low < high:
pi = partition(arr,low,high)
quickSort(arr, low, pi-1)
quickSort(arr, pi+1, high)
arr = [10, 7, 8, 9, 1, 5]
n = len(arr)
quickSort(arr,0,n-1)
print ("排序后的数组:")
for i in range(n):
print ("%d" %arr[i],end=",")
下面以数列a={30,40,60,10,20,50}为例,演示它的快速排序过程(如下图)。 上图只是给出了第1趟快速排序的流程。在第1趟中,设置x=a[i],即x=30。
(01) 从"右 --> 左"查找小于x的数:找到满足条件的数a[j]=20,此时j=4;然后将a[j]赋值a[i],此时i=0;接着从左往右遍历。
(02) 从"左 --> 右"查找大于x的数:找到满足条件的数a[i]=40,此时i=1;然后将a[i]赋值a[j],此时j=4;接着从右往左遍历。
(03) 从"右 --> 左"查找小于x的数:找到满足条件的数a[j]=10,此时j=3;然后将a[j]赋值a[i],此时i=1;接着从左往右遍历。
(04) 从"左 --> 右"查找大于x的数:找到满足条件的数a[i]=60,此时i=2;然后将a[i]赋值a[j],此时j=3;接着从右往左遍历。
(05) 从"右 --> 左"查找小于x的数:没有找到满足条件的数。当i>=j时,停止查找;然后将x赋值给a[i]。此趟遍历结束!
按照同样的方法,对子数列进行递归遍历。最后得到有序数组!
def quick_sort(lists,i,j):
if i >= j:
return list
pivot = lists[i]
low = i
high = j
while i < j:
while i < j and lists[j] >= pivot:
j -= 1
lists[i]=lists[j]
while i < j and lists[i] <=pivot:
i += 1
lists[j]=lists[i]
lists[j] = pivot
quick_sort(lists,low,i-1)
quick_sort(lists,i+1,high)
return lists
if __name__=="__main__":
lists=[30,24,5,58,18,36,12,42,39]
print("排序前的序列为:")
for i in lists:
print(i,end =" ")
print("\n排序后的序列为:")
for i in quick_sort(lists,0,len(lists)-1):
print(i,end=" ")
好了今天就学到这里。
4 链表(LinkList)
链表(LinkList) 数据存储在“节点(Node)”中, 节点包含的内容:数据和下一个节点的指针 链表额增删改查的时间复杂度全部是O(n)级别,但是在链表头的操作的时间复杂度是O(1)的 优点:真正的动态,不需要考虑容量的问题 缺点:丧失了随机访问的能力,因为每一个节点通过next指针穿插起来,不是连续存储的
和数组对比 数组最好用于索引有语意的情况。例如scores[2] 是线性结构 数组最大的优点:支持快速查询
链表不适合用于索引有语意的情况 不是线性结构 链表最大的优点:真正的动态,不浪费存储空间 定义节点:
class Node(object):
"""单链表的结点"""
def __init__(self, item):
self.item = item
self.next = None
定义链表:
class SingleLinkList(object):
"""单链表"""
def __init__(self):
self._head = None
创建链表:
if __name__=="__main__":
link_list=SingleLinkList()
node1=Node(1)
node2=Node(2)
link_list._head = node1
node1.next = node2
print(link_list._head.item)
print(link_list._head.next.item)
class Node(object):
"""单链表的结点"""
def __init__(self, item):
self.item = item
self.next = None
class SingleLinkList(object):
"""单链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""判断链表是否为空"""
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 items(self):
"""遍历链表"""
cur = self._head
while cur is not None:
yield cur.item
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, index, item):
"""指定位置插入元素"""
if index <= 0:
self.add(item)
elif index > (self.length() - 1):
self.append(item)
else:
node = Node(item)
cur = self._head
for i in range(index - 1):
cur = cur.next
node.next = cur.next
cur.next = node
def remove(self, item):
"""删除节点"""
cur = self._head
pre = None
while cur is not None:
if cur.item == item:
if not pre:
self._head = cur.next
else:
pre.next = cur.next
return True
else:
pre = cur
cur = cur.next
def find(self, item):
"""查找元素是否存在"""
return item in self.items()
def getIndex(self, data):
if self.is_empty():
print("error : out of index")
return None
cur = self._head
index = 0
while cur is not None:
if cur.item == data:
return index
index += 1
cur = cur.next
return None
def show(self):
if self.is_empty():
print("空链表")
return None
cur = self._head
while cur is not None:
print(cur.item, end="-->")
cur = cur.next
def deleteIdex(self, index):
if index == 0:
self._head = self._head.next
return
elif index > (self.length() - 1):
return
j = 0
node = self._head
pre = self._head
while node.next and j < index:
pre = node
node = node.next
j += 1
if j == index:
pre.next = node.next
if __name__ == '__main__':
link_list = SingleLinkList()
for i in range(5):
link_list.append(i)
link_list.add(6)
for i in link_list.items():
print(i, end='\t')
link_list.insert(3, 9)
print('\n', list(link_list.items()))
link_list.remove(0)
print(link_list.find(4))
print('\n', list(link_list.items()))
link_list.show()
参看imooc上liuyubobobo老师java数据结构的python改写:
class Node:
def __init__(self, elem_=None, next_=None):
"""
节点类构造函数
:param elem_: 节点所带的元素,默认为None
:param next_: 指向下一个节点的标签(在python中叫做标签)
"""
self.elem = elem_
self.next = next_
def printNode(self):
"""打印Node"""
print(self.elem, end=' ')
class LinkedList:
def __init__(self):
"""
链表构造函数
"""
self._dummyhead = Node()
self._size = 0
def getSize(self):
"""
获得链表中节点的个数
:return: 节点个数
"""
return self._size
def isEmpty(self):
"""
判断链表是否为空
:return: bool值,空为True
"""
return self.getSize() == 0
def add(self, index, elem):
"""
普适性的插入功能
时间复杂度:O(n)
:param index: 要插入的位置(注意index对于用户来说也是从零开始的,这里我没做更改)
:param elem: 待插入的元素
"""
if index < 0 or index > self._size:
raise Exception('Add failed. Illegal index')
prev = self._dummyhead
for i in range(index):
prev = prev.next
prev.next = Node(elem, prev.next)
self._size += 1
def addFirst(self, elem):
"""
将elem插入到链表头部
时间复杂度:O(1)
:param elem: 要插入的元素
"""
self.add(0, elem)
def addLast(self, elem):
"""
链表尾部插入元素elem
时间复杂度:O(n)
:param elem: 待插入的元素
"""
self.add(self._size, elem)
def remove(self, index):
"""
删除第index位置的节点
时间复杂度:O(n)
:param index: 相应的位置,注意从零开始
:return: 被删除节点的elem成员变量
"""
if index < 0 or index >= self.getSize():
raise Exception('Remove falied. Illegal index')
pre = self._dummyhead
for i in range(index):
pre = pre.next
retNode = pre.next
pre.next = retNode.next
retNode.next = None
self._size -= 1
return retNode.elem
def removeFirst(self):
"""
删除第一个节点(index=0)
时间复杂度:O(1)
:return: 第一个节点的elem成员变量
"""
return self.remove(0)
def removeLast(self):
"""
删除最后一个节点(index=self._size-1)
时间复杂度:O(n)
:return: 最后一个节点的elem成员变量
"""
return self.remove(self.getSize() - 1)
def removeElement(self, elem):
"""
删除链表的指定元素elem,这个方法实现的是将链表中为elem的Node全部删除哦,与数组只删除最左边的第一个是不一样的!如果elem不存在我们什么也不做
:param elem: 待删除的元素elem
时间复杂度:O(n)
"""
pre = self._dummyhead
while pre.next:
if pre.next.elem == elem:
delNode = pre.next
pre.next = delNode.next
delNode.next = None
self._size -= 1
else:
pre = pre.next
def get(self, index):
"""
获得链表第index位置的值
时间复杂度:O(n)
:param index: 可以理解成索引,但并不是索引!
:return: 第index位置的值
"""
if index < 0 or index >= self.getSize():
raise Exception('Get failed.index is Valid, index require 0<=index<=self._size-1')
cur = self._dummyhead.next
for i in range(index):
cur = cur.next
return cur.elem
def getFirst(self):
"""
获取链表第一个节点的值
时间复杂度:O(1)
:return: 第一个节点的elem
"""
return self.get(0)
def getLast(self):
"""
获取链表最后一个节点的值
时间复杂度:O(n)
:return: 最后一个节点的elem
"""
return self.get(self.getSize() - 1)
def set(self, index, e):
"""
把链表中第index位置的节点的elem设置成e
时间复杂度:O(n)
:param index: 链表中要操作的节点的位置
:param e: 将要设为的值
"""
if index < 0 or index >= self.getSize():
raise Exception('Set failed.index is Valid, index require 0<=index<=self._size-1')
cur = self._dummyhead.next
for i in range(index):
cur = cur.next
cur.elem = e
def contains(self, e):
"""
判断链表的节点的elem中是否存在e
时间复杂度:O(n)
由于并不存在索引,所以只能从头开始找,一直找。。。如果到尾还没找到就是不存在
:param e: 要判断的值
:return: bool值,存在为True
"""
cur = self._dummyhead.next
while cur != None:
if cur.elem == e:
return True
cur = cur.next
return False
def printLinkedList(self):
"""对链表进行打印操作"""
cur = self._dummyhead.next
print('表头:', end=' ')
while cur != None:
cur.printNode()
cur = cur.next
print('\nSize: %d' % self.getSize())
+++++++++++++++++==+++++++++++++++++=+=++++++++++++++++++++
import linkedlist
import numpy as np
np.random.seed(7)
test = linkedlist.LinkedList()
print(test.getSize())
print(test.isEmpty())
test.addFirst(6)
for i in range(13):
test.addLast(np.random.randint(11))
test.printLinkedList()
test.add(10, 'annihilation7')
test.printLinkedList()
print(test.getSize())
print(test.get(2))
print(test.getLast())
print(test.getFirst())
test.set(0, 30)
test.printLinkedList()
print(test.contains(13))
print(test.remove(8))
test.printLinkedList()
print(test.removeFirst())
test.printLinkedList()
print(test.removeLast())
test.printLinkedList()
print('删除全部为7的元素:')
test.removeElement(7)
test.printLinkedList()
print(test.getSize())
|