算法导论10.3-4:紧凑的双向链表

题目:
我们往往希望双向链表的所有元素在存储器中保持紧凑,例如,在多数组表示中,占用前m个下标位置。(在页式虚拟存储的计算环境下,即为这种情况。)假设除指向聊表本身的指针外没有其他指针指向该链表元素,试说明如何实现过程ALLOCATE-OBJECT和FREE-OBJECT,使得该表保持紧凑。(提示:使用栈的数组实现。)
解答:
某位大牛的算法导论中的答案中,分配链表元素位置时从数组最前向最后分配空间,若删除一个元素,且该元素在存储链表的数组中并非最后一个元素,则需将数组中处于该位置右端的所有链表元素左移。该种方法一次删除操作将消耗线性时间。
本文中采用的策略有所不同。分配链表元素位置时,也是从存储链表的数组的最前断向后分配。有如下约定:
L->free的位置之后的所有数组均为空,包含L->free位置均为可分配的位置。
每次删除除了L->free - 1位置的链表元素时,需将L->free - 1位置的元素移到被删除元素的位置,并将调整该元素前后元素的prev和next指向的位置,接着将L->free减去1,此时仍然保持了L->free和其后的所有数组元素均处于待分配状态。
一些函数和结构体的声明如下:

typedef struct TIGHT_DOUBLE_ARRAY_DOUBLE_LINKED_LIST
{
    int size;
    int count;
    int head;
    int free;
    item_t * key;
    int * next;
    int * prev;
} TDADLL;
void TDADLL_traverse(TDADLL * L);
TDADLL* TDADLL_init(int size);
item_t* TDADLL_get_object(TDADLL * L, int location);
int TDADLL_get_next(TDADLL * L, int location);
void TDADLL_set_next(TDADLL * L, int location, int next);
int TDADLL_get_prev(TDADLL * L, int location);
void TDADLL_set_prev(TDADLL * L, int location, int prev);
void TDADLL_move(TDADLL * L, int src, int dest);
int TDADLL_free(TDADLL * L, int location);
int TDADLL_allocate(TDADLL * L);
int TDADLL_insert(TDADLL * L, item_t item);
int TDADLL_search(TDADLL * L, item_t item);
int TDADLL_delete(TDADLL * L, item_t item);

实现如下:

//functions for tight static double array double linked list
void TDADLL_traverse(TDADLL * L) {
    if (L == NULL) {
        fprintf(stderr, "Not initialized.\n");
        return;
    }
    if (L->head == 0) {
        fprintf(stderr, "Empty linked list.\n");
    }
    int location = L->head;
    printf("Total elements number is %3d. Linked list size is %3d. Linked list head is %d\n", \
        L->count, L->size, L->head);
    while (location != 0) {
        printf("%3d item prev is %3d, location is %3d, next is %3d.\n", \
            TDADLL_get_object(L, location)->key, TDADLL_get_prev(L, location), location, \
            TDADLL_get_next(L, location));
        location = TDADLL_get_next(L, location);
    }
    printf("Free space start postion is %3d\n", L->free);
}
TDADLL* TDADLL_init(int size) {
    TDADLL * L = (TDADLL*)malloc(sizeof(TDADLL));
    if (!L) {
        fprintf(stderr, "Tight static double array double linked list init fail.\n");
        return NULL;
    }
    L->size = size;
    L->count = 0;
    L->head = 0;
    L->free = 1;
    L->key = (item_t*)calloc(size, sizeof(item_t));
    L->prev = (int*)calloc(size, sizeof(int));
    L->next = (int*)calloc(size, sizeof(int));
    if (!L->key || !L->next || !L->prev) {
        fprintf(stderr, "Tighe static double array double linked list content init fail.\n");
        return NULL;
    }
    return L;
}
item_t* TDADLL_get_object(TDADLL * L, int location) {
    return L->key + location - 1;
}
int TDADLL_get_next(TDADLL * L, int location) {
    return L->next[location - 1];
}
void TDADLL_set_next(TDADLL * L, int location, int next) {
    L->next[location - 1] = next;
}
int TDADLL_get_prev(TDADLL * L, int location) {
    return L->prev[location - 1];
}
void TDADLL_set_prev(TDADLL * L, int location, int prev) {
    L->prev[location - 1] = prev;
}
void TDADLL_move(TDADLL * L, int src, int dest) {
    if (src <= 0 || src > L->size || dest <= 0 || dest > L->size) {
        fprintf(stderr, "Move Out of range.\n");
        return;
    }
    L->next[dest - 1] = L->next[src - 1];
    L->prev[dest - 1] = L->prev[src - 1];
    L->key[dest - 1] = L->key[src - 1];
}
int TDADLL_free(TDADLL * L, int location) {
    if (location <= 0 || location > L->size) {
        fprintf(stderr, "Free %d Out of range.\n", location);
        return 0;
    }
    if (location >= L->free && L->free != 0) {
        fprintf(stderr, "Free %d Out of range.\n", location);
        return 0;
    }
    if (L->free == 0) {
        TDADLL_move(L, L->size, location);
        L->free = L->size;
        if (L->size == L->head)
            L->head = location;
    } else if (L->free != location + 1) {
        TDADLL_move(L, L->free - 1, location);
        L->free = L->free -1;
        if (L->free == L->head)
            L->head = location;
    } else {
        L->free--;
        return 1;
    }
    //printf("location: %d free:%d\n", location, L->free);
    int next = TDADLL_get_next(L, location);
    int prev = TDADLL_get_prev(L, location);
    if (next != 0) {
        TDADLL_set_prev(L, next, location);
    }
    if (prev != 0) {
        TDADLL_set_next(L, prev, location);
    }
    return 1;
}
int TDADLL_allocate(TDADLL * L) {
    if (L->free == 0) {
        fprintf(stderr, "Allocatet Out of range.\n");
        return 0;
    }
    int location = L->free;
    if (L->free == L->size) {
        L->free = 0;
    } else {
        L->free++;
    }
    return location;
}
int TDADLL_insert(TDADLL * L, item_t item) {
    if (L == NULL) {
        fprintf(stderr, "Not initialized.\n");
        return 0;
    }
    int location = TDADLL_allocate(L);
    if (location == 0) {
        fprintf(stderr, "Allocate location fail.\n");
        return location;
    }
    TDADLL_set_next(L, location, L->head);
    TDADLL_set_prev(L, location, 0);
    *TDADLL_get_object(L, location) = item;
    if (L->head != 0) {
        TDADLL_set_prev(L, L->head, location);
    }
    L->count++;
    L->head = location;
    return location;
}
int TDADLL_search(TDADLL * L, item_t item) {
    if (L == NULL) {
        fprintf(stderr, "Not initialized.\n");
        return 0;
    }
    if (L->head == 0) {
        fprintf(stderr, "Empty linked list.\n");
        return 0;
    }
    int location = L->head;
    while (location != 0) {
        if (TDADLL_get_object(L, location)->key == item.key) {
            return location;
        }
        location = TDADLL_get_next(L, location);
    }
    fprintf(stderr, "Item %d cannot be found.\n", item.key);
    return 0;
}
int TDADLL_delete(TDADLL * L, item_t item) {
    int location = TDADLL_search(L, item);
    if (location == 0) {
        fprintf(stderr, "Delete %d fail.\n", item.key);
        return 0;
    }
    int next = TDADLL_get_next(L, location);
    int prev = TDADLL_get_prev(L, location);
    //printf("location:%d next:%d prev:%d\n", location, next, prev);
    if (next != 0) {
        TDADLL_set_prev(L, next, prev);
    }
    if (prev != 0) {
        TDADLL_set_next(L, prev, next);
    }
    if (location == L->head) {
        L->head = next;
    }
    L->count--;
    //TDADLL_traverse(L);
    return TDADLL_free(L, location);
}
//--------------------------------------------------------------------------

可用如下代码进行测试:

void test_for_TDADLL() {
    TDADLL * L = TDADLL_init(SIZE);
    item_t item = {0, NULL};
    for (int i = 0; i < SIZE; ++i)
    {
        item.key = i;
        TDADLL_insert(L, item);
    }
    TDADLL_traverse(L);
    for (int i = 0; i < 10; ++i)
    {
        item.key = i + 10;
        TDADLL_delete(L, item);
    }
    TDADLL_traverse(L);
    for (int i = 0; i < SIZE; ++i)
    {
        item.key = i;
        TDADLL_delete(L, item);
    }
    TDADLL_traverse(L);
    printf("-------------------------------------------------------------------\n");
    for (int i = 0; i < SIZE; ++i)
    {
        item.key = i;
        TDADLL_insert(L, item);
    }
    TDADLL_traverse(L);
    for (int i = 0; i < 10; ++i)
    {
        item.key = i + 10;
        TDADLL_delete(L, item);
    }
    TDADLL_traverse(L);
    for (int i = SIZE; i >= -1; --i)
    {
        item.key = i;
        TDADLL_delete(L, item);
    }
    TDADLL_traverse(L);
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 算法6-1:二叉链表存储的二叉树的结构定义 二叉链表存储的二叉树是一种常见的二叉树存储方式,其结构定义如下: typedef struct BiTNode{ TElemType data; // 数据域 struct BiTNode *lchild, *rchild; // 左右孩子指针 }BiTNode, *BiTree; 其中,TElemType为二叉树节点的数据类型,可以根据实际情况进行定义。 算法6-2:二叉链表存储的二叉树的创建 二叉链表存储的二叉树的创建可以通过递归方式实现,具体算法如下: void CreateBiTree(BiTree *T){ TElemType ch; scanf("%c", &ch); // 输入节点的值 if(ch == '#'){ // 如果输入的是#,表示该节点为空 *T = NULL; }else{ *T = (BiTree)malloc(sizeof(BiTNode)); // 创建新节点 (*T)->data = ch; // 节点赋值 CreateBiTree(&((*T)->lchild)); // 递归创建左子树 CreateBiTree(&((*T)->rchild)); // 递归创建右子树 } } 其中,#表示空节点,输入#时,该节点为空。 算法6-3:二叉链表存储的二叉树的遍历 二叉链表存储的二叉树的遍历可以通过递归方式实现,具体算法如下: void PreOrderTraverse(BiTree T){ // 先序遍历 if(T != NULL){ printf("%c ", T->data); // 访问根节点 PreOrderTraverse(T->lchild); // 遍历左子树 PreOrderTraverse(T->rchild); // 遍历右子树 } } void InOrderTraverse(BiTree T){ // 中序遍历 if(T != NULL){ InOrderTraverse(T->lchild); // 遍历左子树 printf("%c ", T->data); // 访问根节点 InOrderTraverse(T->rchild); // 遍历右子树 } } void PostOrderTraverse(BiTree T){ // 后序遍历 if(T != NULL){ PostOrderTraverse(T->lchild); // 遍历左子树 PostOrderTraverse(T->rchild); // 遍历右子树 printf("%c ", T->data); // 访问根节点 } } 其中,先序遍历是先访问根节点,再遍历左子树和右子树;中序遍历是先遍历左子树,再访问根节点和右子树;后序遍历是先遍历左子树和右子树,再访问根节点。 算法6-4:二叉链表存储的二叉树的销毁 二叉链表存储的二叉树的销毁可以通过递归方式实现,具体算法如下: void DestroyBiTree(BiTree *T){ if(*T != NULL){ DestroyBiTree(&((*T)->lchild)); // 销毁左子树 DestroyBiTree(&((*T)->rchild)); // 销毁右子树 free(*T); // 释放节点空间 *T = NULL; // 节点指针置空 } } 其中,递归销毁左子树和右子树,最后释放节点空间并将节点指针置空。 ### 回答2: 二叉链表存储的二叉树是一种常见的二叉树存储结构,它是由一个节点数组和一个指向根节点的指针组成的。每个节点都包含一个值、一个左孩子指针和一个右孩子指针。这种存储结构可以非常方便地进行二叉树的遍历和修改操作,是二叉树基本操作的重要实现方式。 算法6-1是建立二叉链表存储结构的算法,它通过前序遍历和中序遍历来构造一棵二叉树。具体而言,算法6-1会先遍历前序序列找到根节点,并在中序序列中找到根节点的位置,然后递归地建立左子树和右子树。该算法时间复杂度为O(n^2),其中n为二叉树的节点数。算法6-1的缺点是在处理大规模的二叉树时效率较低。 算法6-2是先序遍历的递归算法,它通过递归实现先序遍历。具体而言,先访问根节点,然后递归访问左子树和右子树。该算法时间复杂度为O(n),其中n为二叉树的节点数。 算法6-3是中序遍历的递归算法,它通过递归实现中序遍历。具体而言,先递归访问左子树,然后访问根节点,最后递归访问右子树。该算法的时间复杂度为O(n),其中n为二叉树的节点数。 算法6-4是后序遍历的递归算法,它通过递归实现后序遍历。具体而言,先递归访问左子树和右子树,最后访问根节点。该算法的时间复杂度为O(n),其中n为二叉树的节点数。 总之,二叉链表存储的二叉树是一种非常方便实用的数据结构,可以方便地进行各种遍历和修改操作。同时,基于递归实现的三种遍历算法也非常简洁高效,应用广泛。 ### 回答3: 算法6-1至6-4描述了二叉链表存储的二叉树。二叉链表存储的二叉树是一种基于线性链式结构存储的二叉树,它通过指针关系将每个结点的左右子树联系起来。下面我们将分别对每个算法进行详细解释。 算法6-1:二叉链表存储结构定义。该算法定义了二叉树的结构,主要是通过指针关系分别指向左右子树和父节点。这样的结构便于对二叉树的操作和遍历。 算法6-2:二叉链表存储的建立。该算法通过输入有序序列,依次插入二叉树结点,先从根结点开始比较大小,插入到左右子树中。当插入到空节点时,创建新的结点,通过指针关系连接起来。递归地进行插入操作,直到序列中的所有元素插入完毕。 算法6-3:二叉链表存储的遍历。该算法通过对二叉树的先序、中序和后序遍历进行递归实现。先序遍历需要先访问根节点,然后再对左右子树进行遍历;中序遍历需要先访问左子树,再访问根节点,最后再访问右子树;后序遍历需要先访问左右子树,最后访问根节点。 算法6-4:二叉链表的基本操作。该算法主要包括插入、删除、查找和修改等操作。其中,插入和删除操作需要先定位到相应的结点,然后通过指针关系进行连接或删除。查找操作需要按照二叉树的规律进行查找,找到目标结点后返回其对应的指针。修改操作类似于删除操作,先找到需要修改的结点,然后进行相应的修改操作。 综上所述,二叉链表存储的二叉树是一种便于操作和遍历的数据结构,它通过指针关系将每个结点的左右子树联系起来。该结构的建立、遍历和操作都可以通过递归实现,不仅提高了程序的可读性,还方便了程序员的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值