Day19、双向链表、树(二叉树、遍历、删除结点)

来源:互联网 发布:php array判断 编辑:程序博客网 时间:2024/03/29 13:25

双向链表

1.      p_next + p_prev  不仅可以指向下一个数,也可以指向前一个数

2.      因此,在需要对尾节点进行操作(增加、删除、获得)时,不需要从头进行遍历,从尾部就可以开始,此外,改写函数初始化、增加数字,使其能够灵活地指向前后的数字。

3.      在源文件增加函数,功能遍历打印,因此需要增加指针:p_link->p_cur

4.      在主函数(测试)中,增加从后向前遍历的功能(r_begin)

编写源文件:01link.c

/*

    链表演示

*/

#include <stdlib.h>

#include "01link.h"

//初始化函数

void link_init(link *p_link) {

   p_link->head.p_next = &(p_link->tail);

   p_link->tail.p_next = NULL;

   p_link->tail.p_prev = &(p_link->head);

   p_link->head.p_prev = NULL;

   p_link->p_cur = NULL;

}

//清理函数

void link_deinit(link *p_link) {

   while (p_link->head.p_next != &(p_link->tail)) {

       node *p_first = &(p_link->head);

       node *p_mid = p_first->p_next;

       node *p_last = p_mid->p_next;

       p_first->p_next = p_last;

       p_last->p_prev = p_first;

       free(p_mid);

       p_mid = NULL;

    }

   p_link->p_cur = NULL;

}

//获得有效数字个数的函数

int link_size(const link *p_link) {

   int cnt = 0;

   const node *p_node = NULL;

   for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {

       const node *p_first = p_node;

       const node *p_mid = p_first->p_next;

       const node *p_last = p_mid->p_next;

       if (p_mid != &(p_link->tail)) {

           cnt++;

       }

    }

   return cnt;

}

//在链表头加入数字

void link_add_head(link *p_link, int num) {

   node *p_first = NULL, *p_mid = NULL, *p_last = NULL;

   node *p_tmp = (node *)malloc(sizeof(node));

   if (!p_tmp) {

       return ;

    }

   p_tmp->num = num;

   p_tmp->p_next = NULL;

   p_tmp->p_prev = NULL;

   p_first = &(p_link->head);

   p_mid = p_first->p_next;

   p_last = p_mid->p_next;

    p_first->p_next = p_tmp;

   p_tmp->p_prev = p_first;

   p_tmp->p_next = p_mid;

   p_mid->p_prev = p_tmp;

   p_link->p_cur = NULL;

}

//在链表末尾追加数字

void link_append(link *p_link, int num) {

   node *p_first = NULL, *p_mid = NULL, *p_last = NULL;

   node *p_tmp = (node *)malloc(sizeof(node));

   if (!p_tmp) {

       return ;

    }

   p_tmp->num = num;

   p_tmp->p_next = NULL;

   p_tmp->p_prev = NULL;

   p_first = p_link->tail.p_prev;

   p_mid = p_first->p_next;

   p_last = p_mid->p_next;

   p_first->p_next = p_tmp;

   p_tmp->p_prev = p_first;

   p_tmp->p_next = p_mid;

   p_mid->p_prev = p_tmp;

   p_link->p_cur = NULL;

}

/*void link_append(link *p_link, int num) {

   node *p_tmp = (node *)malloc(sizeof(node));

   node *p_node = NULL;

   if (!p_tmp) {

       return ;

    }

   p_tmp->num = num;

   p_tmp->p_next = NULL;

   p_tmp->p_prev = NULL;

   for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {

       node *p_first = p_node;

       node *p_mid = p_first->p_next;

       node *p_last = p_mid->p_next;

       if (p_mid == &(p_link->tail)) {

           p_first->p_next = p_tmp;

           p_tmp->p_prev = p_first;

           p_tmp->p_next = p_mid;

           p_mid->p_prev = p_tmp;

           break;

       }

    }

   p_link->p_cur = NULL;

}*/

//按顺序加入数字的函数

void link_insert_in_order(link *p_link, intnum) {

   node *p_node = NULL;

   node *p_tmp = (node *)malloc(sizeof(node));

   if (!p_tmp) {

       return ;

    }

   p_tmp->num = num;

   p_tmp->p_next = NULL;

   p_tmp->p_prev = NULL;

   for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {

       node *p_first = p_node;

       node *p_mid = p_first->p_next;

       node *p_last = p_mid->p_next;

       if (p_mid == &(p_link->tail) || p_mid->num > num) {

           p_first->p_next = p_tmp;

           p_tmp->p_prev = p_first;

           p_tmp->p_next = p_mid;

           p_mid->p_prev = p_tmp;

           break;

       }

    }

   p_link->p_cur = NULL;

}

//删除最前边数字的函数

void link_remove_head(link *p_link) {

   node *p_first = NULL, *p_mid = NULL, *p_last = NULL;

   p_first = &(p_link->head);

   p_mid = p_first->p_next;

   p_last = p_mid->p_next;

   p_first->p_next = p_last;

   p_last->p_prev = p_first;

    free(p_mid);

   p_mid = NULL;

   p_link->p_cur = NULL;

}

//删除最后一个结点的函数

void link_remove_tail(link *p_link) {

   if (p_link->head.p_next != &(p_link->tail)) {

       node *p_first = p_link->tail.p_prev->p_prev;

       node *p_mid = p_first->p_next;

        node *p_last = p_mid->p_next;

       p_first->p_next = p_last;

       p_last->p_prev = p_first;

       free(p_mid);

       p_mid = NULL;

    }

   p_link->p_cur = NULL;

}

/*void link_remove_tail(link *p_link) {

   node *p_node = NULL;

   for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {

       node *p_first = p_node;

       node *p_mid = p_first->p_next;

       node *p_last = p_mid->p_next;

       if (p_last == &(p_link->tail)) {

           p_first->p_next = p_last;

           p_last->p_prev = p_first;

           free(p_mid);

           p_mid = NULL;

           break;

       }

    }

   p_link->p_cur = NULL;

}*/

//获得最前面数字的函数

int link_get_head(const link *p_link, int*p_num) {

   if (p_link->head.p_next == &(p_link->tail)) {

       return 0;

    }

   else {

       *p_num = p_link->head.p_next->num;

       return 1;

    }

}

//获得最后一个数字的函数

int link_get_tail(const link *p_link, int*p_num) {

   if (p_link->head.p_next == &(p_link->tail)) {

       return 0;

    }

   else {

       *p_num = p_link->tail.p_prev->num;

       return 1;

    }

}

/*int link_get_tail(const link *p_link, int*p_num) {

   node *p_node = NULL;

   for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {

       node *p_first = p_node;

       node *p_mid = p_first->p_next;

       node *p_last = p_mid->p_next;

       if (p_last == &(p_link->tail)) {

           *p_num = p_mid->num;

           return 1;

       }

    }

   return 0;

}*/

//开始从前向后遍历所有结点

void link_begin(link *p_link) {

   p_link->p_cur = &(p_link->head);

}

//在遍历过程中获得下一个数字

int link_next(link *p_link, int *p_num) {

   if (!(p_link->p_cur)) {

       return 0;

    }

   p_link->p_cur = p_link->p_cur->p_next;

   if (p_link->p_cur == &(p_link->tail)) {

       p_link->p_cur = NULL;

       return 0;

    }

   else {

       *p_num = p_link->p_cur->num;

       return 1;

    }

}

//开始从后向前依次遍历链表中所有结点

void link_rbegin(link *p_link) {

   p_link->p_cur = &(p_link->tail);

}

//在从后先前遍历过程中获得前一个数字

int link_prev(link *p_link, int *p_num) {

   if (!(p_link->p_cur)) {

       return 0;

    }

   p_link->p_cur = p_link->p_cur->p_prev;

   if (p_link->p_cur == &(p_link->head)) {

       return 0;

    }

   else {

       *p_num = p_link->p_cur->num;

       return 1;

    }

}

改成头文件:

#ifndef   __01LINK_H__

#define   __01LINK_H__

typedef struct node {

   int num;

   struct node *p_next;

   struct node *p_prev;

} node;

typedef struct {

   node head, tail;

   node *p_cur;

} link;

//初始化函数

void link_init(link *);

//清理函数

void link_deinit(link *);

//获得有效数字个数的函数

int link_size(const link *);

//在链表头加入数字

void link_add_head(link *, int );

//在链表末尾追加数字

void link_append(link *, int );

//按顺序加入数字的函数

void link_insert_in_order(link *, int );

//删除最前边数字的函数

void link_remove_head(link *);

//删除最后一个结点的函数

void link_remove_tail(link *);

//获得最前面数字的函数

int link_get_head(const link *, int *);

//获得最后一个数字的函数

int link_get_tail(const link *, int *);

//开始从前向后遍历所有结点

void link_begin(link *);

//在遍历中获得下一个结点的数字

int link_next(link *, int *);

//开始从后向前遍历所有结点

void link_rbegin(link *);

//获得前一个数字的函数

int link_prev(link *, int *);

#endif  //__01LINK_H__

编写主函数:

#include <stdio.h>

#include "01link.h"

int main() {

   int num = 0;

   link lnk = {0};

   link_init(&lnk);

   link_add_head(&lnk, 10);

   link_append(&lnk, 100);

   link_insert_in_order(&lnk, 70);

   link_insert_in_order(&lnk, 20);

   link_insert_in_order(&lnk, 40);

   link_insert_in_order(&lnk, 30);

   link_insert_in_order(&lnk, 50);

   printf("数字个数是%d\n", link_size(&lnk));

   link_begin(&lnk);

   while (1) {

      if (link_next(&lnk, &num)) {

          printf("%d ", num);

      }

      else {

          break;

      }

    }

   printf("\n");

   link_remove_head(&lnk);

   link_remove_tail(&lnk);

   link_get_head(&lnk, &num);

   printf("最前边的数字是%d\n", num);

   link_get_tail(&lnk, &num);

   printf("最后边的数字是%d\n", num);

   printf("数字个数是%d\n", link_size(&lnk));

   link_begin(&lnk);

   while (1) {

      if (link_next(&lnk, &num)) {

          printf("%d ", num);

      }

      else {

           break;

      }

    }

   printf("\n");

   link_rbegin(&lnk);

   while (1) {

       if (link_prev(&lnk, &num)) {

           printf("%d ", num);

       }

       else {

           break;

       }

    }

   printf("\n");

   link_deinit(&lnk);

   return 0;

}

 

 

如果单向线性链式物理结构中每个节点可以找到多个其它结点,就成了树

树里的所有结点可以分为几层,不同层之间符合线性规律(任意两层之间有前后顺序)

数的最上面一层应该只有一个结点,这个结点叫做树的根结点

如果树里两个结点之间有直接联系,这种联系叫做父子关系。其中靠近根结点的叫做父结点,另外一个叫子结点。

任何结点最多有一个父结点(根结点没有父结点)

如果任何一个结点最多只有两个子结点,则这种树叫做二叉树

二叉树是最简单的树

二叉树中某个结点的两个子结点分别叫做它的左子结点右子结点

二叉树里任何结点都可以作为根结点代表一棵二叉树

一个结点的左子结点代表的树叫做这个结点的左子树,右子结点代表的树叫做它的右子树

树的操作大多数都是通过遍历实现

通常采用递归函数实现树的遍历

(一棵树分为三部分:根、左子树、右子树,对左子树和对右子树的处理也是对树的处理,所以要使用递归)

遍历时左子树一定在右子树前面处理

前序遍历:若树为空,则空操作返回。先访问根结点,然后前序遍历左子树,再前序遍历右子树

中序遍历:若树为空,则空操作返回。首先中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树

后序遍历:若树为空,则空操作返回。从左到右先叶子后结点的方式遍历访问左右字树,最后访问根结点

有序二叉树:任何结点上左子树的值都比它本身小,右子树比本身数字大

练习:对树排序

                          50

25                                                                                                 80

    13           37              61             93

 7

编写源文件2.c

#include<stdlib.h>

#include"2.h"

//初始化函数

void tree_init(tree *p_tree){//指向一棵数

    p_tree->p_node=NULL;

}

//清理函数

void tree_deinit(tree *p_tree){//    采用后序遍历清理,用递归函数

   if(!(p_tree)){

       return ;       //若树为空,则空操作返回

    }

   tree_deinit(&(p_tree->p_node->left));//先清理左子树

   tree_deinit(&(p_tree->p_node->right));//再清理右子树

   free(p_tree->p_node);//最后释放根结点

   p_tree->p_node=NULL;

}

//在有序二叉树里查找某个数字所在的位置

tree *tree_search_in_order(const tree*p_tree,int num){//采用前序遍历容易找,因为是有序二叉树,左子树的值比根结点小,右子树的值比根结点的值大

   if(!(p_tree->p_node)){

       return (tree *)p_tree; //显式声明

    }

   if(p_tree->p_node->num==num){ //找根结点

       return (tree *)p_tree;  //显式声明,没有警告

    }

   else if(p_tree->p_node->num>num){ //找左边 

       return tree_search_in_order(&(p_tree->p_node->left),num);

    }

   else{   //找右边

       return tree_search_in_order(&(p_tree->p_node->right),num);

    }

}

//在有序二叉树里插入新数字

void tree_insert_in_order(tree *p_tree,intnum){

   tree *p_tr=tree_search_in_order(p_tree,num);//先查找新数字是否存在

   if(p_tr->p_node){

       return ;         //如果存在,直接返回

    }

   node *p_tmp=(node *)malloc(sizeof(node));//如果不存在,分配内存

   if(!p_tmp){                         //内存分配失败,返回

       return ;

    }

   p_tmp->num=num;    //内存分配成功,把新数字赋值给新结点

   p_tmp->left.p_node=NULL;

   p_tmp->right.p_node=NULL;

   p_tr->p_node=p_tmp;

}

//中序遍历打印函数  

void tree_miter(tree*p_tree,void(*p_func)(int)/* 函数指针 */){  

// 因为是有序二叉树,中序遍历可以使其从小到大排列

   if(!(p_tree->p_node)){

       return ;

    }

   tree_miter(&(p_tree->p_node->left),p_func);//递归调用处理左子树

   p_func(p_tree->p_node->num);//处理根结点

   tree_miter(&(p_tree->p_node->right),p_func);//递归调用处理右子树

}

//前序遍历函数

void tree_fiter(tree *p_tree,void(*p_func)(int)){

   if(!(p_tree->p_node)){

      return ;

    }

   p_func(p_tree->p_node->num);

   tree_fiter(&(p_tree->p_node->left),p_func);

   tree_fiter(&(p_tree->p_node->right),p_func);

}

//后序遍历函数

void tree_liter(tree *p_tree,void(*p_func)(int)){

   if(!(p_tree->p_node)){

      return ;

    }

   tree_fiter(&(p_tree->p_node->left),p_func);

   tree_fiter(&(p_tree->p_node->right),p_func);

   p_func(p_tree->p_node->num);

}

//从有序二叉树里删除一个数字的函数

void tree_remove(tree *p_tree,int num){

   node *p_remove=NULL,*p_left=NULL,*p_right=NULL;

   //    要删的结点         其左子树,    其右子树

   tree *p_tmp=tree_search_in_order(p_tree,num);

   if(!(p_tmp->p_node)){ //首先如果这数不存在,则不采取任何操作

       return ;

    }

   p_remove=p_tmp->p_node;

   p_left=p_remove->left.p_node;

   p_right=p_remove->right.p_node;

   if(!p_left){ //如果左子树为空

       p_tmp->p_node=p_right;//把右子树当作结点

    }

   else if(!p_right){//如果右子树为空

       p_tmp->p_node=p_left;//把左子树当作结点

    }

   else{//两个子树都存在时

       tree*p_tmp1=tree_search_in_order(&(p_remove->left),p_right->num);

//找到要删除结点其右子树的值在左子树的位置,赋给tmp1结点

       p_tmp1->p_node=p_right;//将右子树插到左子树(上个语句找的位置)里

 

       p_tmp->p_node=p_left;//把左子树的结点替代原树(要删除的结点)的位置

    }

   free(p_remove);

   p_remove=NULL;

}

编写头文件:2.h

#ifndef   __2_H__

#define   __2_H__

struct node;

//提前告诉编译器这个node结构体存在,不能与下面的结构体颠倒,只能把结构体指针放在前面,此时struct node *p_node占4个字节,下面的Node结构体的字节个数不确定

typedef struct{

   struct node *p_node;//方块

}tree;

typedef struct node{  //圆圈

   int num;

   tree left;        //圆圈里包含两个方块

   tree right;

}node;

//初始化函数

void tree_init(tree *);//指向一棵数

//清理函数

void tree_deinit(tree *);//    采用后序遍历清理,用递归函数

 

tree *tree_search_in_order(const tree*,int);//采用前序遍历容易找,因为是有序二叉树,左子树的值比其父结点小,右子树的值比其父结点的值大

void tree_insert_in_order(tree *,int );

//中序遍历打印函数  

void tree_miter(tree *,void(*)(int)) ;

void tree_fiter(tree *p_tree,void(*p_func)(int));

void tree_liter(tree *p_tree,void(*p_func)(int));

void tree_remove(tree *,int );

#endif   //__2_H__

编写主函数2main.c:

#include<stdio.h>

#include"2.h"

void print_cb(int num){

   printf("%d ",num);

}

int main(){

   tree tr={0};

   tree_init(&tr);

   tree_insert_in_order(&tr,50);

   tree_insert_in_order(&tr,25);

   tree_insert_in_order(&tr,80);

   tree_insert_in_order(&tr,13);

   tree_insert_in_order(&tr,37);

   tree_insert_in_order(&tr,61);

   tree_insert_in_order(&tr,93);

   tree_insert_in_order(&tr,7);

   tree_remove(&tr,25);

   tree_miter(&tr,print_cb);

   printf("\n");

   tree_deinit(&tr);

    return 0;

}

输入指令:gcc2main.c 2.c

 

删掉一个数:    

         50

25                             80

    13           37           61             93

 7

假如删除25

则树的图形变为:    

                         50

          13                         80

7            37         61                93

                        50

37                                                                                       80

    13                    61                 93

  7

在源函数增加函数:

58 void tree_remove(tree *p_tree,int num){

 59    node *p_remove=NULL,*p_left=NULL,*p_right=NULL;

 60    //    要删的结点         其左子树,    其右子树

 61     tree*p_tmp=tree_search_in_order(p_tree,num);

 62    if(!(p_tmp->p_node)){ //首先如果这数不存在,则不采取任何操作

 63        return ;

 64     }

 65    p_remove=p_tmp->p_node;

 66    p_left=p_remove->left.p_node;

 67    p_right=p_remove->right.p_node;

 68    if(!p_left){ //如果左子树为空

 69        p_tmp->p_node=p_right;//把右子树当作结点

 70     }

 71    else if(!p_right){//如果右子树为空

 72        p_tmp->p_node=p_left;//把左子树当作结点

 73     }

 74     else{//两个子树都存在时

 75         tree*p_tmp1=tree_search_in_order(&(p_remove->left),p_right->num);

 76//找到要删除结点其右子树的值在左子树的位置,赋给tmp1结点

 77        p_tmp1->p_node=p_right;//将右子树插到左子树(上个语句找的位置)里

 78        

 79        p_tmp->p_node=p_left;//把左子树的结点替代原树(要删除的结点)的位置

 80     }

 81     free(p_remove);

 82     p_remove=NULL;

 83 }

在头文件增加函数声明:

22 void tree_remove(tree *,int );

主函数增加一条语句变为:

  6int main(){

 7     tree tr={0};

 8     tree_init(&tr);

 9    tree_insert_in_order(&tr,50);

 10    tree_insert_in_order(&tr,25);

 11    tree_insert_in_order(&tr,80);

 12    tree_insert_in_order(&tr,13);

 13    tree_insert_in_order(&tr,37);

 14    tree_insert_in_order(&tr,61);

 15    tree_insert_in_order(&tr,93);

 16    tree_insert_in_order(&tr,7);

 17    tree_remove(&tr,25);

 18    tree_miter(&tr,print_cb);

 19    printf("\n");

 20    tree_deinit(&tr);

 21    return 0;

 22 }

 

 

在源函数增加前序和后序遍历功能

57 //前序遍历函数

 58void tree_fiter(tree *p_tree,void (*p_func)(int)){

 59    if(!(p_tree->p_node)){

 60       return ;

 61     }

 62    p_func(p_tree->p_node->num);

 63    tree_fiter(&(p_tree->p_node->left),p_func);

 64    tree_fiter(&(p_tree->p_node->right),p_func);

 65 }

 66//后序遍历函数

 67void tree_liter(tree *p_tree,void (*p_func)(int)){

 68    if(!(p_tree->p_node)){

 69       return ;

 70     }

 71    tree_fiter(&(p_tree->p_node->left),p_func);

 72     tree_fiter(&(p_tree->p_node->right),p_func);

 73    p_func(p_tree->p_node->num);

 74 }

在头文件添加函数声明:

22 void tree_fiter(tree *p_tree,void(*p_func)(int));

23 void tree_liter(tree *p_tree,void(*p_func)(int));

 

1 0
原创粉丝点击