数据结构期末考试总结

尽全力去准备程刚的期末考试,没想到还是得不到90+的分数,心情郁闷。不过,试卷还是极有水平的,下面我就把所有当时感觉困难的题目,都放在这里详细剖析一下,以供学习参考:

2.链式栈使用什么结构?
类比数组栈,可以想象出链表栈的结构啊。由于头进头出,所以链表构成的栈只需设置一个top pointer指向队列开头,整个栈使用单链表即可。

什么时候使用double linked-list?
能回溯(双向移动)是其最大的优点

Following are advantages/disadvantages of doubly linked list over singly linked list.

Advantages over singly linked list
1) A DLL can be traversed in both forward and backward direction.
2) The delete operation in DLL is more efficient if pointer to the node to be deleted is given.
In singly linked list, to delete a node, pointer to the previous node is needed. To get this previous node, sometimes the list is traversed. In DLL, we can get the previous node using previous pointer.

Disadvantages over singly linked list
1) Every node of DLL Require extra space for an previous pointer. It is possible to implement DLL with single pointer though (See this and this).
2) All operations require an extra pointer previous to be maintained. For example, in insertion, we need to modify previous pointers together with next pointers. For example in following functions for insertions at different positions, we need 1 or 2 extra steps to set previous pointer.

综上所述,使用double-linked list对于stack这种单指针操作(插入或删除)的数据结构来说纯粹是浪费额外空间,single-linked list完全可以胜任。

7.二分搜索树怎么搜索?
此题存疑,根据二分搜索范围不断缩小的特点。我认为有两个答案都不可能出现。

11.前、中、后序的线索树,哪个找不到后继?
一般说来,thread binary tree都指的是按inorder遍历下的顺序来构建predecessor, successor的。对于inorder来说,构建线索的思路如下:

先找current node的左子树最右节点,有的话即为predecessor,不用构建thread。没有左子树才构建thread
再找current node的右子树最左节点,有的话即为successor,不用构建thread。没有右子树才构建thread

构建thread binary tree完成后,能加快中序遍历的访问速度,操作与构建相反:先看current node有无right thread,如有就直接跳到其线索后继;没有时才使用leftMost()函数找其右子树的最左节点(没建立线索的后继)

以下代码演示single-thread binary tree的情况(只为建立successor thread, 不考虑前驱)

struct Node 
{
    int data;
    Node *left, *right;
    bool rightThread;  
}

// Utility function to find leftmost node in a tree rooted with n
struct Node* leftMost(struct Node *n)
{
    if (n == NULL)
       return NULL;

    while (n->left != NULL)
        n = n->left;

    return n;
}

// C code to do inorder traversal in a threaded binary tree
void inOrder(struct Node *root)
{
    struct Node *cur = leftmost(root);
    while (cur != NULL)
    {
        printf("%d ", cur->data);

        // If this node is a thread node, then go to
        // inorder successor
        if (cur->rightThread)
            cur = cur->rightThread;
        else // Else go to the leftmost child in right subtree
            cur = leftmost(cur->right);
    }
}

所以,从这个意义上来说,也就明白了为什么thread binary tree一般都用于inorder traversal的顺序情况。当试图构建preorder thread tree时,知道后继所在却不知道前驱,或者说,每一个节点前驱都要用thread来指明。postorder thread tree则相反,知道前驱却不知道后继。

We could also base a scheme on post-order. Unfortunately, forward post-order will always be hard, because a node’s post-order successor is never below it in the tree. However, a scheme to support reverse post-order would be somewhat easier!

12.4个权值构建二分搜索树?
这里用到卡特兰数来解决问题,The first few Catalan numbers for n = 0, 1, 2, 3, … are 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862,……所以本题答案为14种可能。

Catalan number Cn = (2n)!/(n+1)!*n!

卡特兰数用于以下三种情形(陈刚只讲过第一种,出栈可能种类数):

1) Count the number of expressions containing n pairs of parentheses which are correctly matched. For n = 3, possible expressions are ((())), ()(()), ()()(), (())(), (()()).

2) Count the number of possible Binary Search Trees with n keys.

3) Count the number of full binary trees (A rooted binary tree is full if every vertex has either two children or no children) with n+1 leaves.

这里有更加详细的计算道理

1.尾递归的定义?
若这个函数在尾位置调用本身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归,是递归的一种特殊情形。

尾调用的重要性在于它可以不在调用栈上面添加一个新的堆栈帧——而是更新它,如同迭代一般。尾递归因而具有两个特征:

调用自身函数(Self-called);
计算仅占用常量栈空间(Stack Space)。

Why do we care?
The tail recursive functions considered better than non tail recursive functions as tail-recursion can be optimized by compiler. The idea used by compilers to optimize tail-recursive functions is simple, since the recursive call is the last statement, there is nothing left to do in the current function, so saving the current function’s stack frame is of no use.

10.Two-way merge算法,第i轮中first sequence长度?
2^i(2的i次方)
为什么是first sequence?因为整个数组长度可能为奇数,合并时second sequence长度可能会差1

degree为2的点的graph,一定有cycle?(中间的一道题,忘记题号……)
这里的degree概念只针对无向图而言,一条edge带来2个degree。不考虑每个vertex的入度和出度分别(那是在有向图中)

In graph theory, the degree (or valency) of a vertex of a graph is the number of edges incident to the vertex, with loops counted twice.

Prove that every undirected finite graph with vertex degree of at least 2 has a cycle

3.给出最大堆,如何进行排序?
看youtube视频演示
需要注意的是,heap中的每一步调整(两个node交换)都对应相应队列array的2个元素进行交换。特别地,删除heap中最大node时,意味着array首尾两元素的交换。heap sort,排序结束标志为heap中只剩下一个node。

1.机器人走路,使用中间变量写递归

#include 

using namespace std;

#define M 3
#define N 3

int countStep(int row, int column, int matrix[M][N]) 
{
    if (row==M-1 && column == N-1)
    {
        return 1;
    }
    int temp1 = 0;
    int temp2 = 0;
    if (row + 1 < M)
    {
        if (matrix[row + 1][column] != -1)
        {
            temp1 = matrix[row + 1][column];
        }
        else {
            temp1 = countStep(row + 1, column, matrix);
        }

    }
    if (column + 1 < N)
    {
        if (matrix[row][column + 1] != -1)
        {
            temp2 = matrix[row][column + 1];
        }
        else {
            temp2 = countStep(row, column + 1, matrix);
        }
    }
    int total = temp1 + temp2;
    matrix[row][column] = total;
    return total;
}



int main()
{
    int matrix[M][N];
    memset(matrix, -1, sizeof(int)*M*N);

    int count = countStep(0, 0, matrix);
    cout << "All the possible ways are: "<system("pause");
    return 0;
}

2.二路归并,如何不用额外数组来排序?
使用memcpy, 向后整体复制解决问题

#include 

using namespace std;
// Merges two subarrays of arr[].
// First subarray is arr[l..m]
// Second subarray is arr[m+1..r]
void merge(int arr[], int left, int middle, int right)
{
    int i, j, k;
    int left_length = middle - left + 1;
    int right_length = right - middle;

    /* Merge the temp arrays back into arr[l..r]*/
    i = left; // Initial index of first subarray
    j = middle+1; // Initial index of second subarray

    int swap_count = 0;
    while (i<= right && j <= right)
    {
        if (arr[i] > arr[j])
        {
            int temp = arr[j];
            memcpy(&arr[i + 1], &arr[i], (left_length - swap_count)*sizeof(int));
            arr[i] = temp;

            i++;
            j++;
        }
        else {
            i++;
            swap_count++;
        }

    }
}

/* l is for left index and r is right index of the
sub-array of arr to be sorted */
void mergeSort(int arr[], int left, int right)
{
    if (left < right)
    {
        // Same as (l+r)/2, but avoids overflow for
        // large l and h
        int middle = left + (right - left) / 2;

        // Sort first and second halves
        mergeSort(arr, left, middle);
        mergeSort(arr, middle + 1, right);

        merge(arr, left, middle, right);
    }
}

/* UTILITY FUNCTIONS */
/* Function to print an array */
void printArray(int A[], int size)
{
    int i;
    for (i = 0; i < size; i++)
        printf("%d ", A[i]);
    printf("\n");
}

/* Driver program to test above functions */
int main()
{
    int arr[] = { 12, 11, 13, 5, 6, 7 };
    int arr_size = sizeof(arr) / sizeof(arr[0]);

    printf("Given array is \n");
    printArray(arr, arr_size);

    mergeSort(arr, 0, arr_size - 1);

    printf("\nSorted array is \n");
    printArray(arr, arr_size);

    system("pause");
    return 0;
}

数据结构教程网站推荐

你可能感兴趣的:(学习经验)