Starting from:

$25

CS5800-Homework 7 Solved

1             Simple Data Structure
1.1           Stack and Queue
Both are dynamic sets in which the element removed from the set by DELETE operation is prespecified. In a stack, the element deleted from the set is the one most recently inserted: the stack implements a last-in, first-out. or LIFO, policy. In a queue, the element deleted is always the one that has been in the set for the longest time: the queue implements a first-in, first-out, or FIFO, policy.

1.2           Linked list
A linked list is a data structure in which the objects are arranged in a linear order. Unlike an array, however, in which the linear order is determined by the array indices, the order in a linked list is determined by a pointer in each object.

1.3       Representing rooted trees

Binary tree and rooted trees with unbounded branching.

2             Binary Search Tree
Basic operation on a binary search tree take time proportional to the height of the tree. For a complete binary tree with n nodes, such operations run in Θ(lgn) worst-case time. If the tree is a linear chain of n nodes, however, the same operations take Θ(n) worst time.

2.1           What is a binary tree
Theorem 12.1: If x is the root of an n-node subtree, then the call INORDERTREE-WALK(x) takes Θ(n) time.

2.2           Querying a binary search tree
Theorem 12.2: we can implement the dynamic-set operations SEARCH, MINIMUM, MAXIMUM, SUCCESSOR, and PREDECESSOR so that each one runs in Ø(h) time on a binary search tree of height h.

2.3           Insertion and deletion
Theorem 12.3: we can implement the dynamic-set operations INSERT and DELETE so that each one runs in O(h) time on a binary search tree of height h.

2.4           Randomly built binary search trees
Theorem 12.4: The expected height of a randomly built binary search tree on n distinct keys is O(lgn).

3             Exercise 10.1-1
 

Figure 1: image

4             Exercise 10.1-4
When Q.head = Q.tail = 1, the queue is empty, and if we attempt to dequeue an element then the queue underflows. When Q.head = Q.tail + 1, the queue is full, and if we attempt to enqueue an element then the queue overflows.

 

Algorithm 1 Enqueue and dequeue

 

1: function Enqueue(Q,x)

2:                      if Q.head == Q.tail + 1 or (Q.head == 1andQ.tail == Q.length) then

3:                       error”overflow”

4:             end if

5:               Q[Q.tail] = x

6:                 if Q.tail == Q.length then

7:                      Q.tail = 1

8:             else

9:                        Q.tail = Q.tail + 1

10:           end if

11: end function

12:

13: function Dequeue(Q)

14:                if Q.head == Q.tail == 1 then

15:                    error”underflow”

16:           end if

17:            x = Q[Q.head]

18:              if Q.head == Q.length then

19:                    Q.head = 1

20:          else

21:                     Q.head = Q.head + 1

22:           end if

23:            return x

24: end function

 

5             Exercise 10.1-6
 

Algorithm 2 Implement a queue using two stacks

 

1: function Enqueue-Stack(stack1,stack2,value)

2:              stack1.push(value)

3: end function

4:

5: function Dequeue-Stack(stack1,stack2)

6:               if stack2 is not empty then

7:                      return stack2.pop()

8:             else

9:                      while stack1 is not empty do

10:                           stack2.push(stack1.pop())

11:                   end while

12:                    if stack2 is empty then

13:                           return −1

14:                  else

15:                           return stack2.pop()

16:                   end if

17:           end if

18: end function

 

The operation ENQUEUE-STACK is same as pushing an element on to stack 1 and it takes O(1). For DEQUEUE-STACK. we pop an element from stack 2 if stack 2 is not empty; Otherwise, for each element in stack 1 we pop it off, then push it on to stack 2 and finally pop the top item from stack 2. This operation is O(n) in the worst-case.

6             Exercise 10.1-7
 

Algorithm 3 Implement a stack using two queues

 

1: function Push-Queue(queue1,queue2,value)

2:              if queue2 is empty then

3:                        Enqueue(queue1,value)

4:             else

5:                        Enqueue(queue2,value)

6:             end if

7: end function

8:

9: function Pop-Queue(queue1,queue2)

10:            if queue1 is empty and queue2 is empty then

11:                   return −1

12:          else

13:                    DelData ← 0

14:                    if queue2 is not empty then

15:                           len ← queue2.length

16:                            while len > 1 do

17:                                      DelData ← Dequeue(queue2)

18:                                      Enqueue(queue1,DelData)

19:                          end while

20:                              DelData ← Dequeue(queue2)

21:                            return DelData

22:                   end if

23:                    if queue1 is not empty then

24:                           len ← queue1.length

25:                            while len > 1 do

26:                                      DelData ← Dequeue(queue1)

27:                                      Enqueue(queue2,DelData)

28:                          end while

29:                              DelData ← Dequeue(queue1)

30:                            return DelData

31:                   end if

32:           end if

33: end function

 

The implementing a stack using two queues, where pop takes O(n) time and push takes O(1) time. The operation PUSH-QUEUE is same as enqueueing each element as we push it. For POP-QUEUE, we dequeue each element from one of the queues and enqueue another queue in the order, but stopping just before the last element. Then, the single element left in the original queue.

7             Exercise 10.2-2
 

Algorithm 4 Implement a stack using a single linked list

 

1: function Push-List(L,x)

2:               x.next = L.head

3:                 if L.head ̸= NIL then

4:                       L.head.prev = x

5:             end if

6:               L.head = x

7:                x.prev = NIL

8: end function

9:

10: function Pop-List(L,L.head)

11:             x ← L.head

12:               if x.prev ̸= NIL then

13:                      x.prev.next = x.next

14:          else

15:                     L.head ← x.next

16:           end if

17:               if x.next ̸= NIL then

18:                     x.next.prev = x.prev

19:           end if

20: end function

 

The PUSH-LIST operation is same as linked list insertion, and POP-LIST operation is same as linked list deletion.

8             Exercise 10.2-6
This can be implemented by doubly linked list. If both sets are a doubly linked list, we just link the last element of the first list to the first element in the second list. Wherever we have a reference to NIL in list code, we replace it by a reference to the sentinel L.nil lies between the head and tail. .

 

1: function List-Union(L1,L2)

2:                 L2.nil.next.prev = L1.nil.prev

3:                 L1.nil.prev.next = L2.nil.next

4:                 L2.nil.prev.next = L1.nil

5:                 L1.nil.prev = L2.nil.prev

6: end function

 

9             Exercise 10.4-2
 

Algorithm 5 Prints out the key of each node in the binary tree

 

1: function Inorder-Tree-Walk(x)

2:                if x ̸= NIL then

3:                             Inorder-Tree-Walk(x.left)

4:                      print x.key

5:                            Inorder-Tree-Walk(x.right)

6:             end if

7: end function

 

10             Problem 10-1 Comparisons among lists
 
unsorted, singly linked
sorted, singly linked
unsorted, doubly linked
sorted, doubly linked
SEARCH(L,x)
O(n)
O(n)
O(n)
O(n)
INSERT(L,x)
O(1)
O(1)
O(1)
O(1)
DELETE(L,x)
O(n)
O(n)
O(1)
O(1)
SUCCESSOR(L,x)
O(n)
O(1)
O(n)
O(1)
MINIMUM(L)
O(n)
O(1)
O(n)
O(1)
MAXIMUM(L)
O(n)
O(n)
O(n)
O(1)
11             Exercise 12.2-5
Suppose the node x has two children, its predecessor is the maximum value in its left subtree and its successor is the minimum value in its right subtree. If the successor of node x had s left child then it wouldn’t be the minimum element. Depending on the property of binary tree, the successor must not have a left child. Similarly, the predecessor must not have a right child.

12             Exercise 12.2-7
 

Algorithm 6 Finding the successor

 

1: function Tree-Minimum(x)

2:                 while x.left ̸= NIL do

3:                        x = x.left

4:          end while 5:   return x

6: end function

7:

8: function Tree-Successor(x)

9:                 if x.right ̸= NIL then

10:                       return Tree-Minimum(x.right)

11:           end if

12:            y ← x.p

13:                 while y ̸= NIL and x == y.right do

14:                    x = y

15:                    y = x.p

16:          end while 17: return y

18: end function

 

A call to TREE-MINIMUM followed by n − 1 calls to TREE-SUCCESSOR performs the same procedure as INORDER-TREE-WALK does. The algorithm traverse each edge at most twice (once going down the tree and once going up), which takes Θ(n) time. The only time the tree is traversed downward is in code of TREE-MINIMUM, and the only time the tree is traversed upward is in code of TREE-SUCCESSOR when we look for the successor of a node that has no right subtree. Therefore, this algorithm runs in Θ(n) time.

13             Exercise 12.3-3
 

Algorithm 7 Sort a given set of n numbers by first building a binary search tree

 

1: function Tree-Insert(T,x)

2:               y ← NIL

3:              x ← T.root

4:                while x ̸= NIL do

5:                      y ← x

6:                        if z.key < x.key then

7:                               x = x.left

8:                     else

9:                               x = x.right

10:                   end if

11:           end while

12:            z.p ← y

13:               if y == NIL then

14:                    T.root = x

15:          else

16:                     if z.key < y.key then

17:                             y.left = z

18:                  else

19:                            y.right = z

20:                   end if

21:           end if

22: end function

23:

24: function Inorder-Tree-Walk(x)

25:              if x ̸= NIL then

26:                           Inorder-Tree-Walk(x.left)

27:                   print x.key

28:                          Inorder-Tree-Walk(x.right)

29:           end if

30: end function

31:

32: function Tree-Sort(x)

33:            let T be an empty binary search tree

34:             for i = 1 to n do

35:                        Tree-Insert(T,x)

36:           end for

37:                  Inorder-Tree-Walk(T.root)

38: end function

 

The worst-case is that we were inserting in already sorted order, therefore, the worst-case takes Θ(n2). In the best-case, the tree is balanced, which means the height doesn’t exceed O(lg(n)), so the best-case takes Θ(nlg(n)).

More products