zoukankan      html  css  js  c++  java
  • 学习"树"(链式)数据结构

    花了两天时间学习了思成的“树”,来跟大家做一个分享吧。

    先上代码哈

    tree.h

    #ifndef _TREE_H
    #define _TREE_H
    
    typedef struct _node
    {
    	void *data;
    	struct _node * parent;
    	struct _node * child;
    	struct _node * next;
    
    }TREE;
    
    int InsertTree(TREE **t,void *data,int size);
    int DeleteAllTree(TREE *t);
    int DeleteTree(TREE *t);
    TREE * GetTreeByKey(TREE *r,void *key,int(*compare)(void *,void *));
    int PrintTree(TREE *t,void (*print)(void *));
    #endif

    testTree.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include "tree.h"
    
    //定义根目录和当前目录
    TREE * root = NULL,* curDir = NULL;
    
    //处理生成目录的函数
    void ParseMkdir(char * com)//mkdir diralkd
    {
    	if(InsertTree(&curDir,&com[6],strlen(&com[6])+1) == 0)
    		printf("命令操作失败!]n");
    }
    
    int CompareDir(void *data,void *key)
    {
    	return strcmp((char*)data,(char*)key)==0?1:0;
    }
    //处理删除目录的函数
    void ParseRmdir(char * com)
    {
    	TREE *p = GetTreeByKey(root,&com[6],CompareDir);
    	if(p == NULL)
    		printf("文件不存在!");
    	DeleteTree(p);
    }
    //处理切换目录的函数
    void ParseCd(char * com)
    {
    	TREE *p = GetTreeByKey(root,&com[3],CompareDir);
    	if(p == NULL)
    		printf("找不到目录!");
    	else
    		curDir = p;
    }
    
    void PrintDir(void *data){
    	printf("%20s",(char *)data);
    }
    //处理查看目录的函数
    void ParseLs(char * com)
    {
    	if(PrintTree(curDir,PrintDir) == 0)
    		printf("没有数据\n");
    	printf("\n");
    }
    
    
    void main(){
    	//实现windows、linux目录的一些操作
    	char str[1024];
    	InsertTree(&root,"/",2);
    	curDir = root;
    	while(1){
    		//提示可以通过命令做一些操作。
    		printf("mkdir创建目录;rmdir删除目录;cd切换目录;ls查看目录内容;exit退出\n");
    		printf(">>: ");
    		gets(str);
    		fflush(stdin);
    		/*函数原型:extern char *strstr(char *str1, char *str2);
      	功能:找出str2字符串在str1字符串中第一次出现的位置(不包括str2的串结束符)。
      	返回值:返回该位置的指针,如找不到,返回空指针。*/
    
    		//查看输入的内容,实现相应功能
    		//如果输入了且mkdir在行首,
    		if(strstr(str,"mkdir") == str)
    			ParseMkdir(str);//解析dir
    		else if(strstr(str,"rmdir") == str)
    			ParseRmdir(str);
    		else if(strstr(str,"cd") == str)
    			ParseCd(str);
    		else if(strstr(str,"ls") == str)
    			ParseLs(str);
    		else if(strstr(str,"exit") == str)
    		{
    			printf("bye!");
    			exit(0);
    		}else
    			printf("command not find!\n");
    	}
    }

    tree.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include"tree.h"
    
    //创建节点,挂给根目录
    //首先,要将内容放到根(TREE *)下,
    //所以要改变指针变量要通过传如指针的指针实现
    //还要传入放进根中的数据,可以传任何数据,
    //所以定义为无类型的,但是这就必须输入一个
    //这个数据的大小,否则就没法初始化。
    int InsertTree(TREE **t,void *data,int size){
    	//定义新节点
    	TREE *p = (TREE *)malloc(sizeof(TREE));
    	TREE *tmp;
    	if(p == NULL) return 0;
    	memset(p,0,sizeof(TREE));
    	p->data = malloc(size);
    	if(p->data == NULL){
    		free(p);
    		return 0;
    	}
    	memcpy(p->data,data,size);
    
    	//root为空,把新节点插入其下
    	if(*t == NULL)
    		*t = p;
    	//如果它不空,但子节点为空,则插入它子节点下
    	else if((*t)->child == NULL){
    		//别忘了给新节点p的父节点赋值
    		p->parent = *t;
    		(*t)->child = p;
    	}else{
    		p->parent = *t;
    		//这一层有多个节点的,遍历到这层最后一个节点
    		tmp = (*t)->child;
    		while(tmp->next)
    			tmp = tmp->next;
    		//遍历到了这层最后节点了,把相应新节点赋值给它
    		tmp->next = p;
    	}
    	return 1;
    }
    
    int DeleteAllTree(TREE *t){
    	TREE *p = NULL;
    	while(t){
    		//树非空
    		if(t->child != NULL){
    			//把其它的左节点全部挂接到他最左边节点下,
    			//作为他最左边节点的子节点
    			p = t->child->child;
    			while(p->next)
    				p = p->next;
    			//p指向要删除节点的子节点的的子节点的最后一个。
    			p->next = t->child->next;
    			t->child->next = NULL;
    
    		}
    		p = t->child;
    		free(t->data);
    		free(t);
    		t = p;
    	}
    	return 1;
    }
    int DeleteTree(TREE *t){
    	if(t == NULL)return 0;
    	//1.要删除的是根节点,这时候删除整棵树
    	if(t->parent == NULL)
    		;
    	//2.要删除的是最左边的节点,把下一个兄弟节点挂到他的父集的子节点中
    	else if(t == t->parent->child)
    		t->parent->child = t->next;
    	//3.一般情况,找到这个节点前一个
    	else
    	{
    		//找到这个节点的前一个
    		TREE *p = t->parent->child;//这层最左边一个
    		while(p->next != t)
    			p = p->next;
    		//循环后p指向t的前一个。
    		p->next = t->next;
    	}
    	DeleteAllTree(t);
    	return 1;
    }
    //查找树中节点
    TREE * GetTreeByKey(TREE *r,void *key,int(*compare)(void *,void *)){
    	if(r == NULL)
    		return NULL;
    	if(compare(r->data,key))
    		return r;
    	if(GetTreeByKey(r->child,key,compare))
    		return GetTreeByKey(r->child,key,compare);
    	if(GetTreeByKey(r->next,key,compare))
    		return GetTreeByKey(r->next,key,compare);
    	return NULL;
    }
    
    int PrintTree(TREE *t,void (*print)(void *)){
    	int i=0;
    	TREE *p = t->child;
    	while(p)
    	{
    		print(p->data);
    		i++;
    		p = p->next;
    	}
    	return i;
    }

    首先介绍下tree.h文件,老规矩,用#ifndef和#endif块包含需要“条件编译”的内容,可以选择是否编译此段代码。在调试中很有用。其次就是数据结构的声明。

    struct _node
    {
    	void *data;
    	struct _node * parent;
    	struct _node * child;
    	struct _node * next;
    
    }

    树节点是由一个数据域data,一个指向父节点指针,一个指向孩子节点的指针,和一个指向下一个兄弟节点的指针组成。

    让我们来看下程序执行的效果:上图!

    image

    首先我查看了数据为空,然后我先后插入了m,n,o三个节点(注:他们应该分别是树根的子节点,他们互为兄弟:见下图:

    image

    image

    由上面的程序展示可见如下的存储结构

    image

    让我们来首先分析一下testTree.c的代码。

    在main函数当中,首先我们的目标就是利用“树”这种结构,实现上面演示的那样的类windows和Linux的目录操作。首先插入一个根。字符串“/”占用两个字节分别为/和/0。把它作为根节点,让root指向它。

    接着给用户输出一些提示,为了每次输入gets函数获得我们需要的输入,先执行fflush(stdin)清空输入缓冲区,通常是为了确保不影响后面的数据读取(例如在读完一个字符串后紧接着又要读取一个字符,此时应该先执行fflush(stdin);)

    然后利用strstr函数extern char *strstr(char *str1, char *str2);返回该位置的指针

    找出str2字符串在str1字符串中第一次出现的位置

    这里if(strstr(str,"mkdir") == str)的意思是mkdir所在的位置与str所指位置相同(mkdir字符串在行首),说明输入正确,此时调用ParseMkdir(str);函数。

    如果用户输入mkdir m,那么ParseMkdir的参数com就="mkdir m"那么它的第六个位置就是指向m的,即com[6]=”m”执行InsertTree(&curDir,&com[6],strlen(&com[6])+1),传入的参数有当前目录,要插入内容"m"和申请TREE节点data域的空间大小strlen(&com[6])+1。应为strlen求的的只会是1,然后给个位置放\0所以就加1咯。。。

    下面的应该不难理解,就不再赘述。。。

    最后来看核心代码tree.c上图!!

    1.理解插入:

    树的插入

    2 理解删除:

    理解删除节点

    3.最后再解释下查找树内节点(思成的视频里的代码运不起来,改了下思路)

    //查找树中节点
    TREE * GetTreeByKey(TREE *r,void *key,int(*compare)(void *,void *)){
    	if(r == NULL)
    		return NULL;
    	if(compare(r->data,key))
    		return r;
    	if(GetTreeByKey(r->child,key,compare))
    		return GetTreeByKey(r->child,key,compare);
    	if(GetTreeByKey(r->next,key,compare))
    		return GetTreeByKey(r->next,key,compare);
    	return NULL;
    }

    我们先传入树的根root,key是要找的内容,compare是比较TREE的data域是不是和key一样的函数,返回0或1.

    首先我们看r的是不是就是要找的节点,是的话直接返回r

    然后再找它的子节点(可以看到这里有点深度优先的味道),思成视频里返回的是r->child,试了试不对,改成了return GetTreeByKey(r->child,key,compare); ,那是显然的,递归有几层我们怎么可能知道呢?但我们可以相信,递归返回的一定会是要找的节点,对不对。

    最后如果孩子和兄弟,兄弟的孩子孩子的兄弟。。什么都找过了还没找到,那只能返回NULL咯。。

    好,希望大家喜欢我这样细致的解释。。^-^..

  • 相关阅读:
    BooStrap4文档摘录: 1. Layout
    PistgreSQL9.6手册(基础摘录)
    HTML 5与CSS 3权威指南(第3版·上册)
    Excel PPT 2013办公应用从入门到精通
    EclipseWTPWeb应用开发
    Photoshop移动UI设计实用教程(第2版)
    高手速成:EDIUS专业级视频与音频制作从入门到精通
    黑客攻防从入门到精通
    利用Python进行数据分析(原书第2版)
    Linux二进制分析
  • 原文地址:https://www.cnblogs.com/shenerguang/p/2329036.html
Copyright © 2011-2022 走看看