Example:二分法 |
以下递归算法搜索排序数组中的值:
search(v,a,lo,hi):
| Input value v
| array a[lo..hi] of values
| Output true if v in a[lo..hi]
| false otherwise
|
| mid=(lo+hi)/2
| if lo>hi then return false
| if a[mid]=v then
| return true
| else if a[mid]then
| return search(v,a,mid+1,hi)
| else
| return search(v,a,lo,mid-1)
| end if
Cost analysis:
search()
for array of length ia[i..j]
, j (length=0) 数组为空
- C0 = 0
a[i..j]
, i≤j
(length=n)
Thus, binary search is O() or simply O(logn) (why?
总共有n个元素,每次查找的区间大小就是n,n/2,n/4,…,n/2^k(接下来操作元素的剩余个数),其中k就是循环的次数。
由于n/2^k取整后>=1,即令n/2^k=1,
可得k=log2n,(是以2为底,n的对数),所以时间复杂度可以表示O()=O(logn)。
Exercise: Analysis of Algorithms |
求复杂度
enqueue(Q,Elem):
| Input queue Q, element Elem
| Output Q with Elem added at the end
|
| Q.top=Q.top+1
| for all i=Q.top down to 1 do
| Q[i]=Q[i-1]
| end for
| Q[0]=Elem
| return Q
Answer: O(|Q|)
binaryConversion(n):
| Input positive integer n
| Output binary representation of n on a stack
|
| create empty stack S
| while n>0 do
| | push (n mod 2) onto S
| | n=n/2 log n
| end while
| return S
Answer: O(log n)
big-Omega
f(n) ≥ c·g(n) ∀n ≥ n0
big-Theta
c'·g(n) ≤ f(n) ≤ c''·g(n) ∀n ≥ n0
当f(n)渐近小于或等于g(n)时,f(n)属于O(g(n))
当f(n)渐近地大于或等于g(n)时,f(n)属于Ω(g(n))
当f(n)渐近等于g(n)时,f(n)属于Θ(g(n))
以前我们使用数组来实现堆栈:
均匀元素的固定大小集合
可通过索引或“移动”指针访问
“固定大小”方面是一个潜在的问题:
(动态)数组有多大(大…以防万一)
如果满了怎么办?
刚性序列是另一个问题:
在数组中间插入/删除项
使用数组的问题可以通过 单独分配元素 把它们连在一起形成一条“链” 来解决
好处:
插入/删除对列表整体的影响最小
仅使用值所需的空间
要实现“元素链”,需要node包含
值
指向下一个节点的链接
要表示节点的链表,请执行以下操作:
我们需要一个指向第一个节点的指针
每个节点都包含指向下一个节点的指针
最后一个节点中的下一个指针为空
链表比数组更灵活:
值不必在内存中相邻
只需改变指针就可以重新排列值
值的数量可以动态更改
可以按任何顺序添加或删除值
缺点:
指针操作出错并不难
每个值还需要存储指向下一个的指针
makeNode(v)
| Input value v
| Output new linked list node with value v
|
| new.value=v // initialise data
| new.next=NULL // initialise link to next node
| return new // return pointer to new node
Exercise: Creating a Linked List |
Write pseudocode to create a linked list of three nodes with values 1, 42 and 9024.
mylist=makeNode(1) //get a new node
mylist.next=makeNode(42)
(mylist.next).next=makeNode(9024)
要访问当前节点中的数据:p.value
获取指向下一个节点的指针:p.next
要遍历链表,请执行以下操作:
将p设置为指向第一个节点(头部); 检查p指向的节点;将p改为指向下一个节点;当p到达列表末尾时停止(NULL)
Print all elements:
showLL(L):
| Input linked list L
|
| p=L
| while p≠NULL do
| print p.value
| p=p.next
| end while
Time complexity: O(|L|)
❖ Exercise: Traversing a linked list |
What does this code do?
1 p=list
2 while p≠NULL do
3 | print p.value
4 | if p.next≠NULL then
5 | p=p.next.next
6 | else
7 | p=NULL
8 | end if
9 end while
每隔一个输出一个元素
如果p恰好是列表中的最后一个元素,那么p.next.next就不存在。
if语句确保我们不会试图将未定义的值赋给第5行中的指针p。
//判断next是否存在,存在把next.next赋值给p输出
❖ Exercise: Traversing a linked list |
Rewrite showLL()
as a recursive function.
showLL(L):
| Input linked list L
| if L≠NULL do
| print L.value
| showLL(L.next)
| end if
//在最前端插入
insertLL(L,d):
Input linked list L, value d
Output L with d prepended to the list
new=makeNode(d) // create new list element
new.next=L // link to beginning of list
return new // new element is new head
O(1)
//Delete the first element:
deleteHead(L):
Input non-empty linked list L, value d
Output L with head deleted
return L.next // move to second element
//Delete a specific element (recursive version):
deleteLL(L,d): | Input linked list L | Output L with element d deleted | | if L=NULL then // element not in list | return L | else if L.value=d then // d found at front | return deleteHead(L) // delete first element | else // delete element in tail list | L.next=deleteLL(L.next,d) | end if | return L
Time complexity: O(|L|)
Exercise: Implementing a Queue as a Linked List |
基于链表为队列开发数据结构,以便…
元素排队需要固定的时间
元素出列需要固定的时间
Dequeue from the front …
dequeue(Q): | Input non-empty queue Q | Output front element d, dequeued from Q | | d=Q.front.value // first element in the list | Q.front=Q.front.next // move to second element | return d
Enqueue at the rear …
enqueue(Q,d): | Input queue Q | | new=makeNode(d) // create new list element | Q.rear.next=new // add to end of list | Q.rear=new // link to new end of list
array | linked list | |
insert/delete at beginning | O(n) | O(1) |
insert/delete at end | O(1) |
O(1) ("doubly-linked" list, with pointer to rear) |
insert/delete at middle | O(n) | O(n) |
find an element | O(n) (O(log n), if array is sorted) |
O(n) |
index a specific element | O(1) | O(n) |
Classes of problems:
简单示例:检查整数n是否为素数
生成/测试n的所有可能因素
如果他们都不通过测试⇒ n是素数
生成过程很简单:
生成从2到n-1的所有数字的序列
测试也很简单:
检查下一个数字是否正好除以n