概念
计算机科学
计算机科学是对问题、解决问题以及解决问题的过程中产生的解决方案的研究。
计算机科学也被认为是对算法的研究,其实算法就是对问题进行处理求解的一种实现思路或者思想。
形象的理解算法
比如古代打仗,需要有战略,目的是在最短时间内并且低成本低消耗的情况下取得战争胜利。
将打仗比喻成编码,那么程序员就是指挥官,你是否可以将你的程序在最短时间且消耗资源最小的情况下获取最终的执行效果呢?算法就是解决问题的策略。
算法的意义
数据结构和算法思想的通用性异常的强大,在任何语言中都被使用,它们将会是我们编码生涯中伴随我们最长久的利器。有一定经验的程序员最终拼的就是算法和数据结构。
数据结构和算法思想也可以帮助我们拓展和历练编码思维,可以让我们更好的融入到编程世界的角角落落。
什么是算法分析
在编码过程中可能会将自己的程序和别人写的程序作对比,在对比过程中可能会发现双方编写的程序很相似但又各不相同。同样也会有一个问题或者是现象,两组程序解决一个问题,代码不相同,可是那一组程序更好呢。
# 简单的案例
a+b+c = 1000
a**2+b**2 = c**2
a,b,c都是自然数
求a,b,c可能的组合?
# 解法一
for a in range(0,1001):
for b in range(0,1001):
for c in range(0,1001):
if a+b+c == 1000 and a**2 + b**2 == c**2:
print(a,b,c)
# 解法二
for a in range(0,1001):
for b in range(0,1001):
c = 1000 - a - b
if a+b+c == 1000 and a**2 + b**2 == c**2:
print(a,b,c)
评判程序优劣的方法
1、消耗计算机资源情况和执行效率(并不直观)
2、计算算法执行的耗时
3、时间复杂度(推荐)
时间复杂度
1、评判规则:量化算法执行的操作/执行步骤的数量
2、最重要的项:时间复杂度表达式中最有意义的项
3、使用大O记法来表示时间复杂度:O(算法执行步骤数量表达式中最有意义的一项)
常见的时间复杂度:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
数据结构
概念:对于数据(基本类型的数据(int、float、char))的组织方式就被称为数据结构。数据结构解决的就是一组数据如何进行保存,保存形式是怎样的。
使用不同的形式组织数据,在基于查询时的时间复杂度是不一样的。因此认为算法是为了解决实际问题而设计的,数据结构是算法需要处理问题的载体。
栈
特性:先进后出的数据结构
栈顶,栈尾
栈的分析与应用:
- 分析:和栈相关的最有用的想法之一来自对它的观察。假设从一个干净的桌面开始,现在把书一本本叠起来,你在构造一个栈。考虑下移除一本书会发生什么。移除的顺序跟刚刚被放置的顺序相反。栈之所以重要是因为它能反转项的顺序。插入跟删除顺序相反。
- 应用:每个 web 浏览器都有一个返回按钮。当你浏览网页时,这些网页被放置在一个栈中(实际是网页的网址)。你现在查看的网页在顶部,你第一个查看的网页在底部。如果按‘返回’按钮,将按相反的顺序浏览刚才的页面。
Python中实现栈
- 栈的抽象数据类型定义:栈的抽象数据类型应该由以下结构和操作定义。栈操作如下:
-
- Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈。
- push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容。
- pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改。
- peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈。
- isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。
- size() 返回栈中的 item 数量。不需要参数,并返回一个整数。
class Stack():
def __init__(self):
self.items = []
def push(self,item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return len(self.items) - 1
def isEmpty(self):
return self.items == []
def size(self):
return len(self.items)
stack = Stack()
print(stack.isEmpty())
stack.push(1)
stack.push(2)
stack.push(3)
# print(stack.size())
# print(stack.pop())
# print(stack.pop())
# print(stack.pop())
print(stack.peek())
浏览器访问模拟
s = Stack()
def getRequest(url):
s.push(url)
def showCurenrUrl():
print('当前页面展示的url:'+s.pop())
def back():
print('回退按钮点击后显示的url:',s.pop())
getRequest('www.1.com')
getRequest('www.2.com')
getRequest('www.3.com')
showCurenrUrl()
back()
back()
队列
特性:先进先出
应用场景:
我们的计算机实验室有 30 台计算机与一台打印机联网。当学生想要打印时,他们的打印任务与正在等待的所有其他打印任务“一致”。第一个进入的任务是先完成。如果你是最后一个,你必须等待你前面的所有其他任务打印。
Python中实现队列:
队列的抽象数据类型定义:队列的抽象数据类型应该由以下结构和操作定义。队列操作如下:
-
- Queue() 创建一个空的新队列。 它不需要参数,并返回一个空队列。
- enqueue(item) 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。
- dequeue() 从队首移除项。它不需要参数并返回 item。 队列被修改。
- isEmpty() 查看队列是否为空。它不需要参数,并返回布尔值。
- size() 返回队列中的项数。它不需要参数,并返回一个整数。
class Queue():
def __init__(self):
self.items = []
def enqueue(self,item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def isEmpty(self):
return self.items == []
def size(self):
return len(self.items)
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
print(q.dequeue())
print(q.dequeue())
print(q.dequeue())
案例:烫手的山芋
烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
q = Queue() kids = ['A','B','C','D','E','F'] for kid in kids: q.enqueue(kid) while q.size() > 1: #内层循环是用来将手里有山芋的孩子排在队头 for i in range(6): kid = q.dequeue() q.enqueue(kid) q.dequeue() print(q.dequeue())
# E
双端队列
特性:同同列相比,有两个头部和尾部。可以在双端进行数据的插入和删除,提供了单数据结构中栈和队列的特性
Python中实现双端队列
Deque的抽象数据类型定义:Deque的抽象数据类型应该由以下结构和操作定义。其中元素可以从首部或尾部的任一端添加和移除。Deque操作如下:
-
- Deque() 创建一个空的新 deque。它不需要参数,并返回空的 deque。
- addFront(item) 将一个新项添加到 deque 的首部。它需要 item 参数 并不返回任何内容。
- addRear(item) 将一个新项添加到 deque 的尾部。它需要 item 参数并不返回任何内容。
- removeFront() 从 deque 中删除首项。它不需要参数并返回 item。deque 被修改。
- removeRear() 从 deque 中删除尾项。它不需要参数并返回 item。deque 被修改。
- isEmpty() 测试 deque 是否为空。它不需要参数,并返回布尔值。
- size() 返回 deque 中的项数。它不需要参数,并返回一个整数。
class Dequeue():
def __init__(self):
self.items = []
#从队列头部插入数据
def addFont(self,item):
self.items.insert(0,item)
#从队列尾部插入数据
def addRear(self,item):
self.items.append(item)
def removeFont(self):#队头取出元素
return self.items.pop()
def removeRear(self):#队尾取元素
return self.items.pop(0)
def size(self):
return len(self.items)
q = Dequeue()
q.addFont(1)
q.addFont(2)
q.addFont(3)
# print(q.removeFont())
# print(q.removeFont())
# print(q.removeFont())
print(q.removeRear())
print(q.removeRear())
print(q.removeRear())
# 3 2 1
案例:回文检查
回文是一个字符串,读取首尾相同的字符,例如,radar toot madam。
def isHuiWen(s):
de = Dequeue()
ex = True
for sr in s:
de.addFont(sr)
while de.size() > 1:
if de.removeFont() != de.removeRear():
ex = False
break
return ex
print(isHuiWen('abcba'))
顺序表和链表
内存:
用来存储和运算二进制的数据
变量:
- a = 10 在计算机中表示的含义(内存图) - 内存空间开辟好了之后,每一块内存空间都会有两个默认的属性 - 内存空间的大小:算机内存大小的单位 - 内存空间的地址:十六进制的数值 - 地址作用:用来定位(追踪)指定内存空间,就可以获取该内存空间中存储的数据 - 变量本质表示的是内存空间的地址,通常变量想要表示的就是内存地址对应的内存空间
- 理解a=10的内存图(引用,指向) - 引用:变量 - 指向:如果某一个变量/引用存储了某一块内存空间的地址后,则表示该变量指向了该内存地址所对应的内存空间。
- 不同数据占用内存空间的大小
单链表
class Node(): # 节点类 item是值 def __init__(self, item): self.item = item self.next = None class Link(): # 构造一个空链表 def __init__(self): # _head永远指向第一个节点的地址 self._head = None def add(self, item): # 增加 # 构建一个节点 item=item next = None node = Node(item) node.next = self._head self._head = node def travel(self): # 遍历 # cur指向了第一个节点 cur = self._head while cur: print(cur.item) cur = cur.next def length(self): count = 0 cur = self._head while cur: count += 1 cur = cur.next return count def append(self, item): # 增加 node = Node(item) # 链表为空 if self.isEmpty(): self._head = node return # 链表非空 cur = self._head # cur前面的一个节点地址 pre = None while cur: pre = cur cur = cur.next pre.next = node def isEmpty(self): return self._head == None def search(self, item): find = False cur = self._head while cur: if cur.item == item: find = True break else: cur = cur.next return find def insert(self, pos, item): length = self.length() if pos <= 0 or pos > length: print('pos错误') return # pos 添加的位置(从1开始) node = Node(item) cur = self._head pre = None for i in range(1, pos): pre = cur cur = cur.next pre.next = node node.next = cur def remove(self, item): cur = self._head pre = None if self._head == None: return # 删除的是第一个节点的情况 if self._head == None: self._head = self._head.next return # 删除的是非第一个节点 while cur: if cur.item == item: pre.next = cur.next break else: pre = cur cur = cur.next link = Link() link.add(1) link.add(2) link.add(3) link.append(4) link.append(5) link.insert(3, 9) link.travel()
二分查找法
有序列表对于我们的实现搜索是很有用的。在顺序查找中,当我们与第一个元素进行比较时,如果第一个元素不是我们要查找的,则最多还有 n-1 个元素需要进行比较。 二分查找则是从中间元素开始,而不是按顺序查找列表。 如果该元素是我们正在寻找的元素,我们就完成了查找。 如果它不是,我们可以使用列表的有序性质来消除剩余元素的一半。如果我们正在查找的元素大于中间元素,就可以消除中间元素以及比中间元素小的一半元素。如果该元素在列表中,肯定在大的那半部分。然后我们可以用大的半部分重复该过程,继续从中间元素开始,将其与我们正在寻找的内容进行比较。
alist = [1, 3, 4, 5, 6, 7, 8, 9, 12, 15, 19]
def search(alist, item):
find = False
low = 0
high = len(alist) - 1
while low <= high:
mid = (low + high) // 2
if alist[mid] < item:
low = mid + 1
elif alist[mid] > item:
high = mid - 1
else:
find = True
break
return find
print(search(alist,6))
python数据结构性能分析
Python中使用列表和字典操作的大O性能。通过基于时间的实验来验证数据结构的花销和使用这些数据结构的好处
未完待续。。。