《算法导论》笔记 第10章 10.2 链表

来源:互联网 发布:淘宝客成交不计入权重 编辑:程序博客网 时间:2024/06/05 10:59

【笔记】

哨兵不能降低各种操作的渐进时间界,但可以降低常数因子,简化代码。

template <typename ITEM>class list {private:    struct DATA{        ITEM key;        DATA *prev, *next;        DATA() {}        DATA(ITEM x):key(x) {}    }NIL;    DATA *nil;    int sz;public:    list(){        nil = &NIL;        nil->next=nil;        nil->prev=nil;    }    ~list(){        while (sz) remove(nil->next);    }    void insert(ITEM x) {        DATA *p = new DATA(x);        p->next = nil->next;        nil->next->prev = p;        nil->next = p;        p->prev = nil;        sz++;    }    bool search(ITEM k) {        DATA* p = nil->next;        while (p != nil && p->key != k) p = p->next;        return p != nil;    }    void remove(DATA* p) {        p->prev->next = p->next;        p->next->prev = p->prev;        delete p;        sz--;    }};


【练习】

10.2-1 动态集合上的操作INSERT能否用一个单链表在O(1)时间内实现?对DELETE操作呢?

INSERT 能在O(1)内实现,DELETE不能。


10.2-2 用一单链表L实现一个栈,要求PUSH和POP操作的时间仍为O(1)。

const int MAXN = 10;struct NODE{    int key;    NODE *next;    NODE():key(0) {}    NODE(int x):key(x) {}};NODE* head[MAXN];void prepare(int L) {    head[L] = NULL;}NODE* search(int L,int k) {    NODE* p = head[L];    while (p!=NULL && p->key!=k) p = p->next;    return p;}void insert(int L,int x) {    NODE *p = new NODE(x);    p->next = head[L];    head[L] = p;}void remove(int L,NODE *p) {    if (p==NULL) return;    if (head[L] == p) {        head[L] = p->next;        delete p;        return;    }    NODE *t = head[L];    while (t->next!=NULL && t->next!=p) t = t->next;    if (t->next==NULL) return;    t->next = p->next;    delete p;}void push(int L,int x) {    insert(L,x);}int pop(int L) {    if (head[L]==NULL) return -1;    int res = head[L]->key;    remove(L,head[L]);    return res;}

10.2-3 用一单链表L实现一个队列,要求ENQUEUE和DEQUEUE操作的时间仍为O(1)。

struct NODE{    int key;    NODE *next;    NODE():key(0) {}    NODE(int x):key(x) {}};NODE *head[MAXN],*tail[MAXN];void prepare(int L) {    head[L] = NULL;    tail[L] = NULL;}void push(int L,int x) {    NODE *p = new NODE(x);    p->next = NULL;    if (head[L] == NULL) head[L] = p;    if (tail[L] != NULL) tail[L]->next = p;    tail[L] = p;}int pop(int L) {    if (head[L]==NULL) return -1;    int res = head[L]->key;    NODE *p = head[L];    head[L] = head[L]->next;    delete p;    return res;}

10.2-4 如前文所写,LIST-SEARCH'过程的每一次循环迭代都需要做两个测试:一个检查x!=nil[L],一个检查key[x]!=k。说明如何能够在每次迭代中,省去对x!=nil[L]的检查。

将哨兵的值赋为要查找的值,在循环结束后判断找到的结点是否为哨兵即可。


10.2-5 用环形单链表来实现字典操作INSERT、DELETE和SEARCH,并给出它们的运行时间。

O(1) O(n) O(n) ?


10.2-6 动态集合操作UNION以两个不想交的集合S1和S2作为输入,输出集合S=S1∪S2包含了S1和S2的所有元素。该操作常常会破坏S1和S2。说明应如何选用一种数据结构,以便支持在O(1)时间内的UNION操作。

采用链表,head[L]指向表头,tail[L]指向表尾。合并时将tail[S1]->next 指向head[S2]。新集合的表头为head[S1]。


10.2-7 请给出一个θ(n)时间的非递归过程,它对含n个元素的单链表的链进行逆转。除了链表本身占用的空间外,该过程应仅使用固定量的存储空间。

void reverse(int L) {    NODE *pre, *p, *nxt;    pre = NULL;    p = head[L];    nxt = p->next;    while (p) {        if (nxt == NULL) {            head[L] = p;            break;        }        p->next = pre;        pre = p;        p = nxt;        nxt = nxt->next;    }}

*10.2-8 说明如何对每个元素仅用一个指针np[x]来实现双链表。

用异或即可,由a^b^b=a可知,np[x]中储存的是next[x]^prev[x]。

从前向后遍历时,prev[x]已知,令pt为prev[x],next[x]即为np[x]^pt,pt初值存在head[L]中。

从后向前遍历时,next[x]已知,令pt为next[x],prev[x]即为np[x]^pt。


0 0