1、线性表的几个基本组成部分:数组、单向链表、双向链表。
1.数组的特点是:数据是连续的;随机访问速度快。
数组中稍微复杂一点的是多维数组和动态数组。
----对于C语言而言,多维数组本质上也是通过一维数组实现的,
动态数组是指数组的容量能动态增长的数组,需要手动实现提供;
----对于C++而言,STL提供了Vector;
----对于Java而言,Collection集合中提供了ArrayList和Vector。
2.单向链表(单链表)是链表的一种,它由节点组成,
每个节点都包含下一个节点的指针。
单链表的特点是:节点的链接方向是单向的;
相对于数组来说,单链表的的随机访问速度较慢,但是单链表删除/添加数据的效率很高。
3.双向链表(双链表)是链表的一种。
和单链表一样,双链表也是由节点组成,
它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。
所以,从双向链表中的任意一个结点开始,
都可以很方便地访问它的前驱结点和后继结点。
一般我们都构造双向循环链表。
2、栈是一种线性存储结构
1.栈中数据是按照"后进先出(LIFO, Last In First Out)"方式进出栈的。
2.栈中添加/删除数据时,只能从栈顶进行操作。
3.栈通常包括的三种操作:
push – 向栈中添加元素。
peek – 返回栈顶元素。
pop – 返回并删除栈顶元素的操作。
3、队列(Queue),是一种线性存储结构。
它有以下几个特点:
1.队列中数据是按照"先进先出(FIFO, First-In-First-Out)"方式进出队列的。
2.队列只允许在"队首"进行删除操作,而在"队尾"进行插入操作。
3.队列通常包括的两种操作:入队列 和 出队列。
4、常见的树,如二叉搜索树,平衡树,红黑树等。
像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
1.每个节点有零个或多个子节点;
2.没有父节点的节点称为根节点;
3.一个非根节点有且只有一个父节点;
4.除了根节点外,每个子节点可以分为多个不相交的子树。
结点的度:结点拥有的子树的数目。
叶子:度为0的结点。
分支结点:度不为0的结点。
树的度:树中结点的最大的度。
层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1。
树的高度:树中结点的最大层次。
无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。
有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。
1.二叉树是每个节点最多有两个子树的树结构。
----它有五种基本形态:二叉树可以是空集;
根可以有空的左子树或右子树;或者左、右子树皆为空。
2.满二叉树
----高度为h,并且由
2
h
−
1
2^{h}-1
2h−1个结点的二叉树,被称为满二叉树。
3.完全二叉树
1).只有最下面两层结点的度可以小于2,
且最下一层的叶结点集中在靠左的若干位置上这样的二叉树。
2).叶子结点只能出现在最下层和次下层,
且最下层的叶子结点集中在树的左部。
3).一棵满二叉树必定是一棵完全二叉树,
而完全二叉树未必是满二叉树。
4.二叉查找树
----又被称为二叉搜索树。设x为二叉查找树中的一个结点,
x节点包含关键字key,节点x的key值记为key[x]。
如果y是x的左子树中的一个结点,则key[y] <= key[x];
如果y是x的右子树的一个结点,则key[y] >= key[x]。
1).若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2).任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3).任意节点的左、右子树也分别为二叉查找树。
4).没有键值相等的节点(no duplicate nodes)。
5、前序遍历、中序遍历、后序遍历3种方式。
1.前序遍历
若二叉树非空,则执行以下操作:
1)访问根结点;
2)先序遍历左子树;
3).先序遍历右子树。
private void preOrder(BSTNode<T> tree) {
if(tree != null) {
System.out.print(tree.key+" ");
preOrder(tree.left);
preOrder(tree.right);
}
}
public void preOrder() {
preOrder(mRoot);
}
2.若二叉树非空,则执行以下操作:
1). 中序遍历左子树;
2). 访问根结点;
3) .中序遍历右子树。
private void inOrder(BSTNode<T> tree) {
if(tree != null) {
inOrder(tree.left);
System.out.print(tree.key+" ");
inOrder(tree.right);
}
}
public void inOrder() {
inOrder(mRoot);
}
3.后序遍历
若二叉树非空,则执行以下操作:
1).后序遍历左子树;
2).后序遍历右子树;
3).访问根结点。
private void postOrder(BSTNode<T> tree) {
if(tree != null)
{
postOrder(tree.left);
postOrder(tree.right);
System.out.print(tree.key+" ");
}
}
public void postOrder() {
postOrder(mRoot);
}
4.遍历这棵二叉树:1) .前序遍历结果: 3 1 2 5 4 6
2) .中序遍历结果: 1 2 3 4 5 6
3) .后序遍历结果: 2 1 4 6 5 3
5.前驱和后继
节点的前驱:是该节点的左子树中的最大节点。
节点的后继:是该节点的右子树中的最小节点。
6.树的深度优先遍历需要用到额外的数据结构—>栈;
而广度优先遍历需要队列来辅助
6、AVL树是高度平衡的而二叉树
AVL树中任何节点的两个子树的高度最大差别为1。
左边的是AVL树,它的任何节点的两个子树的高度差别都<=1;
右边的不是AVL树,因为7的两颗子树的高度相差为2
(以2为根节点的树的高度是3,而以8为根节点的树的高度是1)。
7、红黑树R-B Tree,全称是Red-Black Tree
一种特殊的二叉查找树。
红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。
1)每个节点或者是黑色,或者是红色。
2)根节点是黑色。
3)每个叶子节点(NIL)是黑色。 [这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
4)如果一个节点是红色的,则它的子节点必须是黑色的。
5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
[确保没有一条路径会比其他路径长出俩倍]
红黑树的应用比较广泛,主要是用它来存储有序的数据,
它的时间复杂度是O(lgn),效率非常之高。
例如,Java集合中的TreeMap和HashMap,都是通过红黑树去实现的。
8、Huffman Tree,哈夫曼树或霍夫曼树,它是最优二叉树。
给定n个权值作为n个叶子结点,构造一棵二叉树,
若树的带权路径长度达到最小,则这棵树被称为哈夫曼树。
- 路径和路径长度
定义:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。
通路中分支的数目称为路径长度。
若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
例子:100和80的路径长度是1,50和30的路径长度是2,20和10的路径长度是3。 - 结点的权及带权路径长度
定义:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
例子:节点20的路径长度是3,它的带权路径长度= 路径长度x权 = 3 x 20 = 60。 - 树的带权路径长度
定义:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
例子:示例中,树的WPL= 1x100 + 2x50 + 3x20 + 3x10 = 100 + 100 + 60 + 30 = 290。
----假设有n个权值,则构造出的哈夫曼树有n个叶子结点。
n个权值分别设为 w1、w2、…、wn,哈夫曼树的构造规则为:
1).将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
2).在森林中选出根结点的权值最小的两棵树进行合并,
作为一棵新树的左、右子树,
且新树的根结点权值为其左、右子树根结点权值之和;
3).从森林中删除选取的两棵树,并将新树加入森林;
4).重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
----以{5,6,7,8,15}为例,来构造一棵哈夫曼树。
第1步:创建森林,森林包括5棵树,这5棵树的权值分别是5,6,7,8,15。
第2步:在森林中,选择根节点权值最小的两棵树(5和6)来进行合并,将它们作为一颗新树的左右孩子(谁左谁右无关紧要,这里,我们选择较小的作为左孩子),并且新树的权值是左右孩子的权值之和。即,新树的权值是11。 然后,将"树5"和"树6"从森林中删除,并将新的树(树11)添加到森林中。
第3步:在森林中,选择根节点权值最小的两棵树(7和8)来进行合并。得到的新树的权值是15。 然后,将"树7"和"树8"从森林中删除,并将新的树(树15)添加到森林中。
第4步:在森林中,选择根节点权值最小的两棵树(11和15)来进行合并。得到的新树的权值是26。 然后,将"树11"和"树15"从森林中删除,并将新的树(树26)添加到森林中。
第5步:在森林中,选择根节点权值最小的两棵树(15和26)来进行合并。得到的新树的权值是41。 然后,将"树15"和"树26"从森林中删除,并将新的树(树41)添加到森林中。
此时,森林中只有一棵树(树41)。这棵树就是我们需要的哈夫曼树!