数据结构与算法学习笔记(七)

本文介绍了二叉树的基本概念,包括节点结构和插入操作。通过put函数实现节点插入,遵循左子节点小于父节点,右子节点大于父节点的原则。同时,文章提供了查找最小值和最大值的函数,以及前序、中序和后序遍历的实现,利用递归调用和队列结构。最后,给出了代码示例及运行结果。
摘要由CSDN通过智能技术生成

1.二叉树

二叉树是一种数据结构,它由节点和指向它们的边组成。每个节点最多有两个子节点,这就是为什么它被称为“二叉树”的原因。左子节点在树上表示比父节点小的数据,右子节点表示比父节点大的数据。每个节点都有一个值,可以是数字、字符或任何其他类型的数据。二叉树每个节点有两个子节点,像颗倒过来的树。

二叉树有点像链表,但没有链表头节点的设立,取而代之作为统领的是根(root),由根衍生出 其余节点。

如图

 在细分的情况下,每一个节点都可看作一个根,有的缺少右子节点,有的两边节点都没,但它们都可看作一颗树。

节点类:

class Node
{
public:
	Node* Left;
	Node* Right;
	string key;
	string value;

	Node(Node* Left, Node* Right,string key, string value)
	{
		this->Left = Left;
		this->Right = Right;
		this->key = key;
		this->value = value;
	}
};

二叉树查找类(主要函数)

class BinaryTree
{
private:
	Node* root;
	int N;
public:
	BinaryTree()
	{
		root = NULL;
		N = 0;
	}
	
	void put(string key, string value)
	{
		root = put(root, key, value);
	}

	Node* put(Node* node, string key, string value)    //下文有解释
	{
		
		if (node == NULL)
		{
			N++;
			Node *new_node= new Node(NULL, NULL, key, value);
			return new_node;
		}
		int result = stoi(key) - stoi(node->key);
		if (result == 0)
		{
			node->value = value;
			return node;
		}
		if (result > 0)
		{
			node->Right = put(node->Right, key, value);
		}
		else
		{
			node->Left = put(node->Left, key, value);
		}
		return node;
	}
……

按照二叉树的特性,左子节点上放比父节点小的数据,右子节点放比父节点大的数据,这里我用key值来表达左右子节点的大小,因此我们需要不断比较key值,此处我的key是string数据类型,利用stoi()函数可以转成整型(要加sstream头文件)。像踢足球一样,比根小就往左踢,反之往右踢,等于直接取代根,直到踢到没人的地方(节点为NULL)的地方,就把球放下(为NULL节点赋值)。

注意到返回值?当节点为NULL时就说明可以赋值了,并返回此节点,因为是节点把自己放入put函数,返回时要给自己赋值。

不是空节点结束的情况,还是一样,都是节点把自己放进put函数,回来时返回自己重复赋值没有影响,主要是迎合空节点赋值。

2.功能函数

最大值和最小值函数

	string minKey()
	{
		Node* min_Node = minKey(root);
		return min_Node->key;
	}

	Node* minKey(Node* node)
	{
		if (node->Left != NULL)
		{
			return minKey(node->Left);
		}
		else
		{
			return node;
		}
	}

	string maxKey()
	{
		Node* n = maxKey(root);
		return n->key;
	}

	Node* maxKey(Node* node)
	{
		if (node->Right != NULL)
		{
			return maxKey(node->Right);
		}
		else
		{
			return node;
		}
	}

还是根据二叉树的特性,最小值永远在最左左子节点,最大值永远在最右右子节点,一边倒的子节点树和只有根的树当我没说(doge。

接下来的三个遍历函数在结尾处有解释,

前序遍历函数(根,左,右):

Queue* preErgodic()
	{
		Queue* list = new Queue();
		preErgodic(root, list);
		return list;
	}

	void preErgodic(Node* node, Queue *list)
	{
		if (node == NULL)
		{
			return;
		}
		list->insert(node->key);
		preErgodic(node->Left, list);
		preErgodic(node->Right, list);
	}

中序遍历函数(左,根,右):

	Queue* midErgodic()
	{
		Queue* list = new Queue();
		midErgodic(root, list);
		return list;
	}
	void midErgodic(Node* node, Queue* list)
	{
		if (node == NULL)
		{
			return;
		}
		if (node->Left != NULL)
		{
			midErgodic(node->Left,list);
		}
		list->insert(node->key);
		if (node->Right != NULL)
		{
			midErgodic(node->Right, list);
		}
	}

后序遍历函数(左,右,根):

Queue* afterErgodic()
	{
		Queue* list = new Queue();
		afterErgodic(root, list);
		return list;
	}
	void afterErgodic(Node* node, Queue* list)
	{
		if (node == NULL)
		{
			return;
		}
		if (node->Left != NULL)
		{
			afterErgodic(node->Left, list);
		}
		
		if (node->Right != NULL)
		{
			afterErgodic(node->Right, list);
		}

		list->insert(node->key);
	}

这三个函数都借用了一个队列结构,把优先级高的值先传入队列中

总的来说,这几个遍历都是先左子节点后右子节点,根节点在其中穿插排序,于是,我们可以采用递归调用,先对左子节点向队列输入,用递归函数将其隔开,以达成优先级。

前序遍历:

先把根节点值录入队列,开始分开两路,左大子树,右大子树,递归调用挡着,先完成左子树才会进行右子树操作,其中递归调用又不断细分,细分到最后左子节点时,先录入节点,再把最后节点的左右子,但都为NULL,直接结束,终于可以对右子节点录入。

其余遍历也一样,建议上手画图直接明了,都可以理解为根来看待,分左根和右根,最后节点只是它的左右子节点是NULL而已。

实现验证:

int main()
{
	BinaryTree tree;
	tree.put("6", "name6");
	tree.put("2", "name2");
	tree.put("3", "name3");
	tree.put("5", "name5");
	tree.put("7", "name7");
	tree.put("6", "name6");

	Queue* list1 = tree.preErgodic();
	cout << "前序遍历:" << endl;
	list1->disList();

	Queue* list2 = tree.midErgodic();
	cout << "中序遍历:" << endl;
	list2->disList();

	Queue* list3 = tree.afterErgodic();
	cout << "后序遍历:" << endl;
	list3->disList();

}

结果:
前序遍历:
有5个节点
6
2
3
5
7
中序遍历:
有5个节点
2
3
5
6
7
后序遍历:
有5个节点
5
3
2
7
6

补队列处代码

#include <iostream>
#include "Nodes.h"

using namespace std;

//纯纯我偷懒,把链表那块函数复制过来用,把定义全挤到头文件里了,但这块不主要,主要还是看二叉树实现

class Queue
{


	Nodes* head;

	int N = 0;

public:

	Queue()
	{
		//初始化头节点和数据
		head = new Nodes("", NULL);

		N = 0;
	};

	void clearList()
	{
		head = NULL;
		N = 0;
	}

	bool isEmpty()
	{
		return N == 0;
	}

	int length()
	{
		return N;
	}

	string get(int i)
	{
		Nodes* n = head->next;
		for (int index = 0; index < N; index++)
		{
			if (n->next == NULL)
			{
				return "NULL";
			}
			n = n->next;
		}
		return n->item;
	}

	void insert(int i, string t)
	{
		//先找到i的前一个节点
		Nodes* pre = head;
		for (int index = 0; index <= i - 1; index++)
		{
			pre = pre->next;
		}
		//找到i处的节点
		Nodes* curr = pre->next;
		//创建新节点,让其指向原i处节点,并让i-1处节点指向它

		Nodes* new_node = new Nodes(t, curr);

		new_node->next = curr;

		pre->next = new_node;

		N++;
	}

	void insert(string t)
	{
		Nodes* n = head;
		while (n->next != NULL)
		{
			n = n->next;
		}

		Nodes* new_node = new Nodes(t, NULL);

		n->next = new_node;

		N++;
	}

	string remove(int i)
	{
		//先找到i的前一个节点
		Nodes* pre = head;
		for (int index = 0; index <= i; index++)
		{
			pre = pre->next;
		}
		//再让i-1处节点指向i+1处节点,并断掉原i节点的指向

		Nodes* curr = pre->next;

		Nodes* next_node = curr->next;

		pre->next = next_node;

		N--;

		return curr->item;
	}

	int indexOf(string t)
	{
		Nodes* n = head->next;
		for (int i = 0; n->next != NULL; i++, n = n->next)//i<n;
		{
			if (n->item == t)
			{
				return i;
			}
		}
		return -1;
	}

	void disList()
	{
		cout << "有" << N << "个节点" << endl;
		Nodes* n = head->next;
		for (int i = 0; i < N; i++)
		{
			cout << n->item << endl;
			n = n->next;
		}
	}
};

队列节点

#include <iostream>

using namespace std;

class Nodes
{
public:
	string item;
	Nodes* next;

	Nodes(string item, Nodes* next)
	{
		this->item = item;
		this->next = next;
	};
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值