rename dir
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
### B树基础
|
||||
|
||||
B树即B-tree(B树就是B-tree),B是balanced,也就是平衡的意思。
|
||||
|
||||
B树又叫平衡多路查找树,节点的直接点个数可以多于2个,一颗m阶的B树:
|
||||
|
||||
1) 树中每个节点做多含有m个子节点
|
||||
2) 根节点不是叶子节点,至少有2个孩子
|
||||
3) 所有叶子节点在同一层
|
||||
4) 除根节点和叶子节点外,每个分支节点至少有[m/2]个子树
|
||||
5) 有j个孩子的非叶子节点有 j-1 个关键码,关键码递增一次排列
|
||||
|
||||
|
||||
### B树应用
|
||||
|
||||
数据库
|
||||
文件系统
|
||||
|
||||
|
||||
### B树存储结构
|
||||
|
||||
|
||||
|
||||
### B树问题和延伸阅读
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* 第一次题目 :构建二叉树(链表存储方式),空格表示空树,实现二叉树的基本操作:创建,遍历(先,中,后,按层)二叉树
|
||||
* 提交邮箱 :[email protected] to [email protected]
|
||||
* 邮件题目 :第一期第一次作业
|
||||
**/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
typedef struct BiTNode{
|
||||
|
||||
char item;
|
||||
struct BiTNode *lChild,*rChild;
|
||||
|
||||
}BiTNode,*BiTree;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ==========================================================================
|
||||
|
||||
|
||||
#define OK 1
|
||||
#define ERROR 0
|
||||
typedef int bool;
|
||||
|
||||
|
||||
typedef BiTree ElemType ; // 也可能是一个复杂的复合类型
|
||||
typedef int Status;
|
||||
|
||||
|
||||
|
||||
//队列的顺序存储
|
||||
typedef struct Node{
|
||||
|
||||
ElemType *elem;
|
||||
ElemType *head;
|
||||
ElemType *tail;
|
||||
int length; // 当前队列的长度
|
||||
int size; // 队列容器的长度,在队列慢得时候可以扩容
|
||||
|
||||
|
||||
}SqQueue;
|
||||
|
||||
//
|
||||
// 在队列采用顺序存储时,有一个毛病,就是队列操作一段时间后,头指针到了队列容器的尾部,而头指针前面的容器内存不可用了,
|
||||
//造成内存极大的浪费,这个问题可以通过循环队列来解决。但是在
|
||||
// 链式队列上则不存在这样的问题
|
||||
//
|
||||
|
||||
|
||||
typedef struct QNode{
|
||||
|
||||
struct QNode *next;
|
||||
ElemType elem;
|
||||
|
||||
}LinkQueue;
|
||||
|
||||
|
||||
// 队列需要维护两个 指针 (队头指针,队尾指针)
|
||||
|
||||
typedef struct{
|
||||
|
||||
LinkQueue *head;
|
||||
LinkQueue *tail;
|
||||
int length;
|
||||
|
||||
}Queue;
|
||||
|
||||
|
||||
Status initQueue(Queue *queue);// 带头结点,没有引用传值,就用指针的指针吧
|
||||
bool isEmpty(Queue *q);
|
||||
int length(Queue *q);
|
||||
|
||||
|
||||
// 在头部插入,尾部删除
|
||||
Status getHead(Queue *q,ElemType *e);
|
||||
Status enQueue(Queue *q,ElemType e);
|
||||
Status deQueue(Queue *q,ElemType *e);
|
||||
|
||||
void traveser(Queue *q);
|
||||
|
||||
|
||||
Status initQueue(Queue *queue){// 带头结点,没有引用传值,就用指针的指针吧
|
||||
|
||||
LinkQueue *lq = (LinkQueue *)malloc(sizeof(LinkQueue));
|
||||
if (!lq) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
lq->elem = NULL;
|
||||
lq->next = NULL;
|
||||
|
||||
(queue)->head = (queue)->tail = lq; // -> 优先级高于 * ,老老实实打上括号
|
||||
(queue)->length = 0;
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
bool isEmpty(Queue *q){
|
||||
|
||||
return (q->head == q->tail);
|
||||
|
||||
}
|
||||
|
||||
int length(Queue *q){
|
||||
|
||||
// int ret;
|
||||
// LinkQueue *p = q->head;
|
||||
// while (p != q->tail) {
|
||||
// ret++;
|
||||
// p = p->next;
|
||||
//
|
||||
// }
|
||||
|
||||
return q->length;
|
||||
|
||||
}
|
||||
|
||||
Status getHead(Queue *q,ElemType *e){
|
||||
|
||||
LinkQueue *p = q->head->next;
|
||||
if (!p) {
|
||||
*e=NULL;
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
*e = p->elem;
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
// 入队(加入到队尾)
|
||||
Status enQueue(Queue *q,ElemType e){
|
||||
|
||||
LinkQueue *newNode = (LinkQueue *)malloc(sizeof(LinkQueue));
|
||||
if (!newNode) {
|
||||
return ERROR;
|
||||
}
|
||||
newNode->elem = e;
|
||||
newNode->next = NULL;
|
||||
q->tail->next = newNode;
|
||||
q->tail = newNode;
|
||||
|
||||
q->length++;
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
//出队(队头). 注意,可能只有1个元素,造成队尾指针丢失
|
||||
Status deQueue(Queue *q,ElemType *e){
|
||||
|
||||
LinkQueue *p = q->head->next;
|
||||
if (!p) {// 队列空
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
*e = p->elem ;
|
||||
}
|
||||
|
||||
LinkQueue *temp = p->next;
|
||||
q->head->next = temp;
|
||||
if (p == q->tail) {
|
||||
q->tail = q->head;
|
||||
}
|
||||
free(p);
|
||||
|
||||
|
||||
q->length--;
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void traveser(Queue *q){
|
||||
|
||||
LinkQueue *p = q->head->next;
|
||||
while (NULL != p) {
|
||||
//printf("%d\n",p->elem);
|
||||
p=p->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ==================================================
|
||||
|
||||
|
||||
|
||||
/*
|
||||
int CreateBiTree(BiTree *T)
|
||||
{
|
||||
|
||||
*T = (BiTNode *)malloc(sizeof(BiTNode));
|
||||
if (*T==NULL)
|
||||
{
|
||||
printf("memery malloc failure !\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("enter data to create node:\n");
|
||||
scanf("%c",&((*T)->item));
|
||||
if((*T)->item=='#'){
|
||||
*T=NULL;
|
||||
}
|
||||
if(*T){
|
||||
printf("创建左子树:\n");
|
||||
CreateBiTree( &((*T)->lChild) );
|
||||
printf("创建右子树:\n");
|
||||
CreateBiTree( &((*T)->rChild) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
// BiTree CreateBiTree()
|
||||
|
||||
// 先序遍历方式创建二叉树
|
||||
BiTree CreateBiTree(){
|
||||
|
||||
char c;
|
||||
BiTNode *tree;
|
||||
|
||||
// 过滤回车键
|
||||
|
||||
|
||||
scanf("%c",&c);
|
||||
if (c==' ')
|
||||
{
|
||||
printf("创建空节点\n");
|
||||
tree = NULL;
|
||||
}else{
|
||||
printf("创建节点 %c\n",c);
|
||||
tree = (BiTNode *)malloc(sizeof(BiTNode));
|
||||
tree->item = c;
|
||||
tree->lChild = CreateBiTree();
|
||||
tree->rChild = CreateBiTree();
|
||||
}
|
||||
|
||||
return tree;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int PreOrderTraverse(BiTree T){
|
||||
|
||||
if(T){
|
||||
printf("%c\n",T->item );
|
||||
PreOrderTraverse(T->lChild);
|
||||
PreOrderTraverse(T->rChild);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InOrderTraverse(BiTree T){
|
||||
|
||||
if (T)
|
||||
{
|
||||
InOrderTraverse(T->lChild);
|
||||
printf("%c\n", T->item);
|
||||
InOrderTraverse(T->rChild);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PostOrderTraverse(BiTree T){
|
||||
|
||||
if (T)
|
||||
{
|
||||
PostOrderTraverse(T->lChild);
|
||||
PostOrderTraverse(T->rChild);
|
||||
printf("%c\n",T->item );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 广度优先遍历 (队列实现)
|
||||
int LevelOrderTraverse(BiTree T){
|
||||
|
||||
if (T)
|
||||
{
|
||||
Queue queue;
|
||||
initQueue(&queue);
|
||||
|
||||
BiTree u;
|
||||
u=(BiTree)malloc(sizeof(BiTNode));
|
||||
enQueue(&queue, T);
|
||||
while(!isEmpty(&queue))
|
||||
{
|
||||
deQueue(&queue, &u);
|
||||
printf("%c",u->item);
|
||||
if(u->lChild)
|
||||
enQueue(&queue, u->lChild);
|
||||
if(u->rChild)
|
||||
enQueue(&queue, u->rChild);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
/* code */
|
||||
BiTree binaryTree;
|
||||
|
||||
printf("创建二叉树,输入\"空格\"创建空节点(先序方式建立二叉树):\n");
|
||||
binaryTree = CreateBiTree();
|
||||
if(binaryTree==NULL){
|
||||
printf("创建空的二叉树\n");
|
||||
}
|
||||
|
||||
|
||||
printf("前序遍历:\n");
|
||||
PreOrderTraverse(binaryTree);
|
||||
printf("\n\n");
|
||||
|
||||
printf("中序遍历:\n");
|
||||
InOrderTraverse(binaryTree);
|
||||
printf("\n\n");
|
||||
|
||||
printf("后序遍历:\n");
|
||||
PostOrderTraverse(binaryTree);
|
||||
printf("\n\n");
|
||||
|
||||
printf("层序遍历:\n");
|
||||
LevelOrderTraverse(binaryTree);
|
||||
printf("\n\n");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// public.h
|
||||
// DS
|
||||
//
|
||||
// Created by mac on 13-9-8.
|
||||
// Copyright (c) 2013年 xiaoran. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef DS_public_h
|
||||
#define DS_public_h
|
||||
|
||||
//#include <malloc.h>
|
||||
#include "stdlib.h" // 后来,换到mac环境下,#include <malloc.h> 应该为<malloc/malloc.h> 或 #include "stdlib.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h> // for memset
|
||||
|
||||
|
||||
typedef char ElemType ; // 也可能是一个复杂的复合类型
|
||||
typedef int Status;
|
||||
|
||||
|
||||
|
||||
#define OK 1
|
||||
#define ERROR 0
|
||||
|
||||
typedef int bool;
|
||||
#define YES 1
|
||||
#define NO 0
|
||||
|
||||
#define DEFAULT_SIZE 10
|
||||
#define OUT_OF_BOUND -1
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// 队列.h
|
||||
// 只在一段进行插入,另一端删除元素
|
||||
//
|
||||
// Created by mac on 13-9-8.
|
||||
// Copyright (c) 2013年 xiaoran. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef DS____h
|
||||
#define DS____h
|
||||
|
||||
#include "public.h"
|
||||
#include "bintree.c"
|
||||
|
||||
typedef BiTNode ElemType;
|
||||
|
||||
//队列的顺序存储
|
||||
typedef struct Node{
|
||||
|
||||
ElemType *elem;
|
||||
ElemType *head;
|
||||
ElemType *tail;
|
||||
int length; // 当前队列的长度
|
||||
int size; // 队列容器的长度,在队列慢得时候可以扩容
|
||||
|
||||
|
||||
}SqQueue;
|
||||
|
||||
//
|
||||
// 在队列采用顺序存储时,有一个毛病,就是队列操作一段时间后,头指针到了队列容器的尾部,而头指针前面的容器内存不可用了,
|
||||
//造成内存极大的浪费,这个问题可以通过循环队列来解决。但是在
|
||||
// 链式队列上则不存在这样的问题
|
||||
//
|
||||
|
||||
|
||||
typedef struct QNode{
|
||||
|
||||
struct QNode *next;
|
||||
ElemType elem;
|
||||
|
||||
}LinkQueue;
|
||||
|
||||
|
||||
// 队列需要维护两个 指针 (队头指针,队尾指针)
|
||||
|
||||
typedef struct{
|
||||
|
||||
LinkQueue *head;
|
||||
LinkQueue *tail;
|
||||
int length;
|
||||
|
||||
}Queue;
|
||||
|
||||
|
||||
Status initQueue(Queue *queue);// 带头结点,没有引用传值,就用指针的指针吧
|
||||
bool isEmpty(Queue *q);
|
||||
int length(Queue *q);
|
||||
|
||||
|
||||
// 在头部插入,尾部删除
|
||||
Status getHead(Queue *q,ElemType *e);
|
||||
Status enQueue(Queue *q,ElemType e);
|
||||
Status deQueue(Queue *q,ElemType *e);
|
||||
|
||||
void traveser(Queue *q);
|
||||
|
||||
|
||||
Status initQueue(Queue *queue){// 带头结点,没有引用传值,就用指针的指针吧
|
||||
|
||||
LinkQueue *lq = (LinkQueue *)malloc(sizeof(LinkQueue));
|
||||
if (!lq) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
lq->elem = 0;
|
||||
lq->next = NULL;
|
||||
|
||||
(queue)->head = (queue)->tail = lq; // -> 优先级高于 * ,老老实实打上括号
|
||||
(queue)->length = 0;
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
bool isEmpty(Queue *q){
|
||||
|
||||
return (q->head == q->tail);
|
||||
|
||||
}
|
||||
|
||||
int length(Queue *q){
|
||||
|
||||
// int ret;
|
||||
// LinkQueue *p = q->head;
|
||||
// while (p != q->tail) {
|
||||
// ret++;
|
||||
// p = p->next;
|
||||
//
|
||||
// }
|
||||
|
||||
return q->length;
|
||||
|
||||
}
|
||||
|
||||
Status getHead(Queue *q,ElemType *e){
|
||||
|
||||
LinkQueue *p = q->head->next;
|
||||
if (!p) {
|
||||
*e=0;
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
*e = p->elem;
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
// 入队(加入到队尾)
|
||||
Status enQueue(Queue *q,ElemType e){
|
||||
|
||||
LinkQueue *newNode = (LinkQueue *)malloc(sizeof(LinkQueue));
|
||||
if (!newNode) {
|
||||
return ERROR;
|
||||
}
|
||||
newNode->elem = e;
|
||||
newNode->next = NULL;
|
||||
q->tail->next = newNode;
|
||||
q->tail = newNode;
|
||||
|
||||
q->length++;
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
//出队(队头). 注意,可能只有1个元素,造成队尾指针丢失
|
||||
Status deQueue(Queue *q,ElemType *e){
|
||||
|
||||
LinkQueue *p = q->head->next;
|
||||
if (!p) {// 队列空
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
*e = p->elem ;
|
||||
}
|
||||
|
||||
LinkQueue *temp = p->next;
|
||||
q->head->next = temp;
|
||||
if (p == q->tail) {
|
||||
q->tail = q->head;
|
||||
}
|
||||
free(p);
|
||||
|
||||
|
||||
q->length--;
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void traveser(Queue *q){
|
||||
|
||||
LinkQueue *p = q->head->next;
|
||||
while (NULL != p) {
|
||||
printf("%d\n",p->elem);
|
||||
p=p->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
### 红黑树基础
|
||||
|
||||
一种自平衡的二叉查找树。在每个节点中增加一个存储位表示节点的颜色,可以是red或black
|
||||
|
||||
红黑树比AVL树优秀在哪?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5个性质
|
||||
|
||||
性质1. 节点是红色或黑色。
|
||||
性质2. 根是黑色。
|
||||
性质3. 所有叶子都是黑色(叶子是NIL节点)。
|
||||
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
|
||||
性质5. 从任一节点到其每个叶子的所有简单路径 都包含相同数目的黑色节点。
|
||||
|
||||
|
||||
|
||||
### 红黑树应用
|
||||
|
||||
|
||||
### 存储结构
|
||||
|
||||
|
||||
### 问题与延伸阅读
|
||||
|
||||
|
||||
*/
|
||||
|
||||
typedef int ElemType
|
||||
|
||||
typedef struct node{
|
||||
|
||||
int color;
|
||||
ElemType key;
|
||||
struct node *lChild,*rChild,*pChild;
|
||||
|
||||
}*RBTree;
|
||||
|
||||
|
||||
int rbtree_insert(RBTree *tree,ElemType key);
|
||||
int rbtree_remove(RBTree *tree,ElemType key);
|
||||
int rbtree_search(RBTree *tree,ElemType key);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
|
||||
### 后缀树(suffix tree)基础
|
||||
|
||||
又叫后缀trie,与trie最大不同在于:字符串集合由指定的后缀子串组成。
|
||||
|
||||
|
||||
很适合用来操作字符串的子串。 用于字符串的匹配和查询
|
||||
|
||||
|
||||
### 后缀树应用
|
||||
|
||||
|
||||
从目标串T中判断是否包含模式串P(时间复杂度接近KMP算法);
|
||||
从目标串T中查找最长的重复子串;
|
||||
从目标串T1和T2中查找最长公共子串;
|
||||
Ziv-Lampel无损压缩算法;
|
||||
从目标串T中查找最长的回文子串;
|
||||
|
||||
|
||||
|
||||
### 存储结构
|
||||
|
||||
|
||||
### suffix tree 问题与延伸阅读
|
||||
|
||||
后缀数组
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
###环境
|
||||
|
||||
|
||||
###编译
|
||||
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,337 @@
|
||||
//
|
||||
// bisearchtree.c
|
||||
// BiSearchTree
|
||||
//
|
||||
// Created by nonstriater on 14-2-22.
|
||||
//
|
||||
//
|
||||
|
||||
#include "bisearchtree.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/**
|
||||
* 创建二叉查找树
|
||||
*
|
||||
* @return 指向一颗空树的指针
|
||||
*/
|
||||
BiSearchTree *bisearch_tree_new(){
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入节点
|
||||
*
|
||||
* @param tree tree
|
||||
* @param node 节点值
|
||||
*/
|
||||
BiSearchTree *bisearch_tree_insert(BiSearchTree *tree,ElemType node){
|
||||
|
||||
BiSearchTree *t = tree;
|
||||
BiSearchTree *parent = NULL;
|
||||
BiSearchTree *newNode = (BiSearchTree *)malloc(sizeof(BiSearchTree));
|
||||
if (NULL==newNode) {
|
||||
printf("malloc failure\n");
|
||||
return tree;
|
||||
}
|
||||
newNode->key = node;
|
||||
newNode->lChild = NULL;
|
||||
newNode->rChild = NULL;
|
||||
|
||||
if (NULL==t) {
|
||||
|
||||
tree = newNode;
|
||||
printf("insert first node %d \n",node);
|
||||
return tree;
|
||||
}
|
||||
|
||||
// 找合适的位置
|
||||
while (t!=NULL) {
|
||||
|
||||
if( node < t->key){
|
||||
parent = t;
|
||||
t=t->lChild;
|
||||
}else if(node == t->key){
|
||||
printf("insert ignore:the bi search tree has the node with %d\n",node);
|
||||
break;
|
||||
}else{
|
||||
parent = t;
|
||||
t=t->rChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent!=NULL) {
|
||||
|
||||
if (node>parent->key) {
|
||||
printf("insert node %d to right of %d\n",node,parent->key);
|
||||
parent->rChild = newNode;
|
||||
|
||||
}else{
|
||||
printf("insert node %d to left of %d\n",node,parent->key);
|
||||
parent->lChild = newNode;
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找节点
|
||||
*
|
||||
* @param tree tree
|
||||
* @param node 节点值
|
||||
* (也可以使用递归方式实现)
|
||||
*/
|
||||
int bisearch_tree_search(BiSearchTree *tree,ElemType node){
|
||||
|
||||
BiSearchTree *t;
|
||||
t= tree;
|
||||
while (NULL!=t) {
|
||||
|
||||
if (node<t->key) {
|
||||
t=t->lChild;
|
||||
}
|
||||
else if (node==t->key) {
|
||||
break;
|
||||
}else{
|
||||
t=t->rChild;
|
||||
}
|
||||
|
||||
}
|
||||
if (NULL==t) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//private
|
||||
int _delete_node(BiSearchTree *node,BiSearchTree*parent){
|
||||
|
||||
//该节点为叶子节点,直接删除
|
||||
if (!node->rChild && !node->lChild)
|
||||
{
|
||||
|
||||
free(node);//父节点???不然野指针,造成崩溃
|
||||
}
|
||||
else if(!node->rChild){ //右子树空则只需重接它的左子树
|
||||
BiSearchTree *target=node->lChild;
|
||||
node->key = node->lChild->key;
|
||||
node->lChild=node->lChild->lChild;
|
||||
node->rChild=node->lChild->rChild;
|
||||
|
||||
free(target);
|
||||
}
|
||||
else if(!node->lChild){ //左子树空只需重接它的右子树
|
||||
BiSearchTree *target=node->rChild;
|
||||
node->key = node->rChild->key;
|
||||
node->lChild=node->rChild->lChild;
|
||||
node->rChild=node->rChild->rChild;
|
||||
|
||||
free(target);
|
||||
}
|
||||
else{ //左右子树均不空
|
||||
|
||||
BiSearchTree *parent=node,*target=node->lChild;
|
||||
while (target->rChild) {
|
||||
parent = target;
|
||||
target=target->rChild;
|
||||
}// 找到左子树最大的,是删除节点的直接“前驱”
|
||||
|
||||
node->key = target->key;
|
||||
|
||||
if (target!=node) {
|
||||
parent->rChild = target->lChild;
|
||||
}else{
|
||||
node->lChild = target->lChild;
|
||||
}
|
||||
|
||||
free(target);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//删除节点,需要重建排序树
|
||||
/*
|
||||
1) 删除节点是叶子节点(分支为0),结构不破坏
|
||||
2)删除节点只有一个分支(分支为1),结构也不破坏
|
||||
3)删除节点有2个分支,此时删除节点
|
||||
|
||||
思路一: 选左子树的最大节点,或右子树最小节点替换
|
||||
|
||||
*/
|
||||
|
||||
int bisearch_tree_delete(BiSearchTree **tree,ElemType node){
|
||||
|
||||
if (NULL==tree) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 查找删除目标节点
|
||||
BiSearchTree *target=*tree,*parent=NULL;
|
||||
while (NULL!=target) {
|
||||
if (node<target->key) {
|
||||
parent=target;
|
||||
target=target->lChild;
|
||||
}else if(node==target->key){
|
||||
break;
|
||||
}else{
|
||||
parent=target;
|
||||
target=target->rChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL==target) {
|
||||
printf("树为空,或想要删除的节点不存在\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//该节点为叶子节点,直接删除
|
||||
if (!target->rChild && !target->lChild)
|
||||
{
|
||||
if (NULL==parent) {////只有一个节点的二叉查找树
|
||||
*tree=NULL;
|
||||
}else{
|
||||
if (target->key>parent->key) {
|
||||
parent->rChild=NULL;
|
||||
}else{
|
||||
parent->lChild=NULL;
|
||||
}
|
||||
|
||||
}
|
||||
free(target);//父节点处理,不然野指针,造成崩溃
|
||||
}
|
||||
|
||||
else if(!target->rChild){ //右子树空则只需重接它的左子树
|
||||
BiSearchTree *del=target->lChild;
|
||||
target->key = target->lChild->key;
|
||||
target->lChild=target->lChild->lChild;
|
||||
target->rChild=target->lChild->rChild;
|
||||
|
||||
free(del);
|
||||
}
|
||||
else if(!target->lChild){ //左子树空只需重接它的右子树
|
||||
BiSearchTree *del=target->rChild;
|
||||
target->key = target->rChild->key;
|
||||
target->lChild=target->rChild->lChild;
|
||||
target->rChild=target->rChild->rChild;
|
||||
|
||||
free(del);
|
||||
}
|
||||
else{ //左右子树均不空
|
||||
|
||||
BiSearchTree *p=target,*t=target->lChild;
|
||||
while (t->rChild) {
|
||||
p = t;
|
||||
t=t->rChild;
|
||||
}// 找到左子树最大的,是删除节点的直接“前驱”
|
||||
|
||||
target->key = t->key;
|
||||
|
||||
if (p!=target) {
|
||||
p->rChild = t->lChild;
|
||||
}else{
|
||||
target->lChild = t->lChild;
|
||||
}
|
||||
|
||||
free(t);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
// if (node==tree->key) {
|
||||
// return _delete_node(tree);
|
||||
// }else if(node>tree->key){
|
||||
// return bisearch_tree_delete(tree->rChild,node);
|
||||
// }else{
|
||||
// return bisearch_tree_delete(tree->lChild,node);
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// 查找删除目标节点
|
||||
BiSearchTree *t=tree,*parent=NULL;
|
||||
while (NULL!=t) {
|
||||
if (node<t->key) {
|
||||
parent=t;
|
||||
t=t->lChild;
|
||||
}else if(node==t->key){
|
||||
break;
|
||||
}else{
|
||||
parent=t;
|
||||
t=t->rChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL==t) {
|
||||
printf("树为空,或想要删除的节点不存在\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// parent 可能为null
|
||||
|
||||
//删除节点0个分支
|
||||
if (!t->lChild && !t->rChild) {
|
||||
if (t->key>parent->key) {
|
||||
parent->rChild=NULL;
|
||||
}else{
|
||||
parent->lChild=NULL;
|
||||
}
|
||||
free(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//删除节点有1个分支
|
||||
else if (t->lChild && !t->rChild) {
|
||||
if (t->key>parent->key) {
|
||||
parent->rChild=t->lChild;
|
||||
}else{
|
||||
parent->lChild=t->lChild;
|
||||
}
|
||||
free(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if (!t->lChild && t->rChild) {
|
||||
if (t->key>parent->key) {
|
||||
parent->rChild=t->rChild;
|
||||
}else{
|
||||
parent->lChild=t->rChild;
|
||||
}
|
||||
free(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//删除节点有2个分支
|
||||
else if (t->lChild && t->rChild) {
|
||||
|
||||
if (t->key->parent->key) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
// 中序遍历节点,也就是从小到大输出
|
||||
void bisearch_tree_inorder_traversal(BiSearchTree *tree){
|
||||
|
||||
if (tree) {
|
||||
bisearch_tree_inorder_traversal(tree->lChild);
|
||||
printf("%d \n",tree->key);
|
||||
bisearch_tree_inorder_traversal(tree->rChild);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// bisearchtree.h
|
||||
// BiSearchTree
|
||||
//
|
||||
// Created by nonstriater on 14-2-22.
|
||||
//
|
||||
//
|
||||
|
||||
/**
|
||||
*
|
||||
二叉查找树(Binary search tree),也叫有序二叉树(Ordered binary tree),排序二叉树(Sorted binary tree)。是指一个空树或者具有下列性质的二叉树:
|
||||
|
||||
1. 若任意节点的左子树不为空,则左子树上所有的节点值小于它的根节点值
|
||||
2. 若任意节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值
|
||||
3. 任意节点左右子树也为二叉查找树
|
||||
4. 没有键值相等的节点
|
||||
|
||||
*/
|
||||
|
||||
#ifndef BiSearchTree_bisearchtree_h
|
||||
#define BiSearchTree_bisearchtree_h
|
||||
|
||||
typedef int ElemType;
|
||||
typedef struct BiSearchTree{
|
||||
|
||||
ElemType key;
|
||||
struct BiSearchTree *lChild;
|
||||
struct BiSearchTree *rChild;
|
||||
|
||||
|
||||
}BiSearchTree;
|
||||
|
||||
|
||||
/**
|
||||
* 创建二叉查找树
|
||||
*
|
||||
* @return 指向一颗空树的指针
|
||||
*/
|
||||
BiSearchTree *bisearch_tree_new();
|
||||
|
||||
/**
|
||||
* 插入节点
|
||||
*
|
||||
* @param tree tree
|
||||
* @param node 节点值
|
||||
*/
|
||||
BiSearchTree *bisearch_tree_insert(BiSearchTree *tree,ElemType node);
|
||||
|
||||
/**
|
||||
* 查找节点
|
||||
*
|
||||
* @param tree tree
|
||||
* @param node 节点值
|
||||
* @return -1失败 0 成功
|
||||
*/
|
||||
int bisearch_tree_search(BiSearchTree *tree,ElemType node);
|
||||
|
||||
//删除节点
|
||||
int bisearch_tree_delete(BiSearchTree **tree,ElemType node);
|
||||
|
||||
// 遍历节点
|
||||
void bisearch_tree_inorder_traversal(BiSearchTree *tree);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// main.c
|
||||
// BiSearchTree
|
||||
//
|
||||
// Created by nonstriater on 14-2-22.
|
||||
//
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bisearchtree.h"
|
||||
|
||||
int main(){
|
||||
|
||||
printf("begining...\n");
|
||||
BiSearchTree *searchTree;
|
||||
searchTree = bisearch_tree_new();
|
||||
searchTree = bisearch_tree_insert(searchTree,19);// 第一次insert
|
||||
bisearch_tree_insert(searchTree,3);
|
||||
bisearch_tree_inorder_traversal(searchTree);
|
||||
|
||||
bisearch_tree_insert(searchTree,122);
|
||||
bisearch_tree_insert(searchTree,55);
|
||||
bisearch_tree_insert(searchTree,65);
|
||||
bisearch_tree_insert(searchTree,180);
|
||||
|
||||
printf("inorder traversal\n");
|
||||
bisearch_tree_inorder_traversal(searchTree);
|
||||
|
||||
|
||||
//=======search ============================================
|
||||
printf("find node...\n");
|
||||
int find = 55;
|
||||
if(bisearch_tree_search(searchTree,find)<0){
|
||||
printf("node %d not exits\n",find);
|
||||
}else{
|
||||
printf("node %d exits\n",find);
|
||||
}
|
||||
|
||||
find = 33;
|
||||
if(bisearch_tree_search(searchTree,find)<0){
|
||||
printf("node %d not exits\n",find);
|
||||
}else{
|
||||
printf("node %d exits\n",find);
|
||||
}
|
||||
|
||||
|
||||
//====== delete,会影响到serachTree值,所以应该传递serachTree的指针 ======================================
|
||||
printf("delete node..\n");
|
||||
find = 4;
|
||||
if(bisearch_tree_delete(&searchTree,find)<0){
|
||||
printf("delete node %d failure\n",find);
|
||||
}else{
|
||||
printf("delete node %d success\n",find);
|
||||
bisearch_tree_inorder_traversal(searchTree);
|
||||
bisearch_tree_insert(searchTree,find);
|
||||
}
|
||||
|
||||
|
||||
find = 65;//叶子
|
||||
if(bisearch_tree_delete(&searchTree,find)<0){
|
||||
printf("delete node failure\n");
|
||||
}else{
|
||||
printf("delete node %d success\n",find);
|
||||
bisearch_tree_inorder_traversal(searchTree);
|
||||
bisearch_tree_insert(searchTree,find);
|
||||
}
|
||||
|
||||
|
||||
|
||||
find = 55;//单分支
|
||||
if(bisearch_tree_delete(&searchTree,find)<0){
|
||||
printf("delete node failure\n");
|
||||
}else{
|
||||
printf("delete node %d success\n ",find);
|
||||
bisearch_tree_inorder_traversal(searchTree);
|
||||
bisearch_tree_insert(searchTree,find);
|
||||
}
|
||||
|
||||
find = 122;//双分支
|
||||
if(bisearch_tree_delete(&searchTree,find)<0){
|
||||
printf("delete node failure\n");
|
||||
}else{
|
||||
printf("delete node %d success\n",find);
|
||||
bisearch_tree_inorder_traversal(searchTree);
|
||||
bisearch_tree_insert(searchTree,find);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
测试结果:
|
||||
|
||||
begining...
|
||||
insert first node 19
|
||||
insert node 3 to left of 19
|
||||
3
|
||||
19
|
||||
insert node 122 to right of 19
|
||||
insert node 55 to left of 122
|
||||
insert node 65 to right of 55
|
||||
insert node 180 to right of 122
|
||||
inorder traversal
|
||||
3
|
||||
19
|
||||
55
|
||||
65
|
||||
122
|
||||
180
|
||||
find node...
|
||||
node 55 exits
|
||||
node 33 not exits
|
||||
delete node..
|
||||
树为空,或想要删除的节点不存在
|
||||
delete node 4 failure
|
||||
delete node 65 success
|
||||
3
|
||||
19
|
||||
55
|
||||
122
|
||||
180
|
||||
insert node 65 to right of 55
|
||||
delete node 55 success
|
||||
3
|
||||
19
|
||||
65
|
||||
122
|
||||
180
|
||||
insert node 55 to left of 65
|
||||
delete node 122 success
|
||||
3
|
||||
19
|
||||
55
|
||||
65
|
||||
180
|
||||
insert node 122 to left of 180
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
## 二叉查找树
|
||||
|
||||
二叉查找树(Binary search tree),也叫`有序二叉树(Ordered binary tree)`,`排序二叉树(Sorted binary tree)`。是指一个空树或者具有下列性质的二叉树:
|
||||
|
||||
1. 若任意节点的左子树不为空,则左子树上所有的节点值小于它的根节点值
|
||||
2. 若任意节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值
|
||||
3. 任意节点左右子树也为二叉查找树
|
||||
4. 没有键值相等的节点
|
||||
|
||||
```
|
||||
typedef int ElemType;
|
||||
typedef struct BiSearchTree{
|
||||
ElemType key;
|
||||
struct BiSearchTree *lChild;
|
||||
struct BiSearchTree *rChild;
|
||||
}BiSearchTree;
|
||||
BiSearchTree *bisearch_tree_insert(BiSearchTree *tree,ElemType node);
|
||||
int bisearch_tree_delete(BiSearchTree **tree,ElemType node);
|
||||
int bisearch_tree_search(BiSearchTree *tree,ElemType node);
|
||||
```
|
||||
|
||||
|
||||
删除节点,需要重建排序树
|
||||
|
||||
1) 删除节点是叶子节点(分支为0),结构不破坏
|
||||
2)删除节点只有一个分支(分支为1),结构也不破坏
|
||||
3)删除节点有2个分支,此时删除节点
|
||||
|
||||
思路一: 选左子树的最大节点,或右子树最小节点替换
|
||||
|
||||
```
|
||||
int bisearch_tree_delete(BiSearchTree **tree,ElemType node){
|
||||
|
||||
if (NULL==tree) {
|
||||
return -1;
|
||||
}
|
||||
// 查找删除目标节点
|
||||
BiSearchTree *target=*tree,*parent=NULL;
|
||||
while (NULL!=target) {
|
||||
if (node<target->key) {
|
||||
parent=target;
|
||||
target=target->lChild;
|
||||
}else if(node==target->key){
|
||||
break;
|
||||
}else{
|
||||
parent=target;
|
||||
target=target->rChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL==target) {
|
||||
printf("树为空,或想要删除的节点不存在\n");
|
||||
return -1;
|
||||
}
|
||||
//该节点为叶子节点,直接删除
|
||||
if (!target->rChild && !target->lChild)
|
||||
{
|
||||
if (NULL==parent) {////只有一个节点的二叉查找树
|
||||
*tree=NULL;
|
||||
}else{
|
||||
if (target->key>parent->key) {
|
||||
parent->rChild=NULL;
|
||||
}else{
|
||||
parent->lChild=NULL;
|
||||
}
|
||||
|
||||
}
|
||||
free(target);//父节点处理,不然野指针,造成崩溃
|
||||
}
|
||||
|
||||
else if(!target->rChild){ //右子树空则只需重接它的左子树,用左子树替换掉当前要删除的节点
|
||||
BiSearchTree *del=target->lChild;
|
||||
target->key = target->lChild->key;
|
||||
target->lChild=target->lChild->lChild;
|
||||
target->rChild=target->lChild->rChild;
|
||||
|
||||
free(del);
|
||||
}
|
||||
else if(!target->lChild){ //左子树空只需重接它的右子树
|
||||
BiSearchTree *del=target->rChild;
|
||||
target->key = target->rChild->key;
|
||||
target->lChild=target->rChild->lChild;
|
||||
target->rChild=target->rChild->rChild;
|
||||
|
||||
free(del);
|
||||
}
|
||||
else{ //左右子树均不空,p,t 2个指针一前以后,将左子树最大的节点(肯定是一个最右的节点)替换到删除的节点后,还需要处理左子树最大节点的左子树
|
||||
|
||||
BiSearchTree *p=target,*t=target->lChild;
|
||||
while (t->rChild) {
|
||||
p = t;
|
||||
t=t->rChild;
|
||||
}// 找到左子树最大的,是删除节点的直接“前驱”
|
||||
|
||||
target->key = t->key;
|
||||
|
||||
if (p!=target) {
|
||||
p->rChild = t->lChild;
|
||||
}else{
|
||||
target->lChild = t->lChild;
|
||||
}
|
||||
|
||||
free(t);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
|
||||
/*
|
||||
自平衡二叉查找树(AVL tree): 首先也是二次查找树,其实 任何2个子树的高度差不大于1
|
||||
在删除,插入的过程中不断调整子树的高度,保证平均和最坏情况下都是O(logn)
|
||||
|
||||
Adelson-Velskii 和 Landis 1962年 创造。
|
||||
|
||||
1) 平衡因子 -1 0 1 节点是正常的。平衡因子 = 左子树高度-右字数高度
|
||||
2) 除此之外的节点是不平衡的,需要重新平衡这个树。也就是AVL旋转
|
||||
|
||||
插入节点:
|
||||
|
||||
a: 左旋转(RR型:节点x的右孩子的右孩子上插入新元素)平衡因子由-1 -》-2 时,需要绕节点x左旋转
|
||||
b:右旋转(LL型:节点X的左孩子的左孩子上插入新元素) 平衡因子有1-》2,右旋转
|
||||
c: 先左后右旋转:(LR型:树中节点X的左孩子的右孩子上插入新元素) 平衡因子从1变成2后,就需要 先绕X的左子节点Y左旋转,接着再绕X右旋转
|
||||
d: 先右后左旋转:(RL型:节点X的右孩子的左孩子上插入新元素)
|
||||
|
||||
|
||||
6 6 6 6
|
||||
/ \ / \
|
||||
5 7 3 9
|
||||
/ \ \ /
|
||||
3 8 5 7
|
||||
(LL型) (RR) (LR) (RL)
|
||||
|
||||
|
||||
删除节点:
|
||||
|
||||
|
||||
|
||||
|
||||
可以看到,为了保证高度平衡,插入和删除操作代价增加
|
||||
|
||||
记于2014-2-28 by @nonstriater
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define LH 1
|
||||
#define EH 0
|
||||
#define RH -1
|
||||
|
||||
|
||||
typedef int KEY_TYPE;
|
||||
typedef struct node{
|
||||
|
||||
KEY_TYPE key;
|
||||
int height; // 平衡因子
|
||||
struct node *lChild;
|
||||
struct node *rChild;
|
||||
|
||||
}AVLTree;
|
||||
|
||||
|
||||
|
||||
//
|
||||
void avltree_rr_rotate(AVLTree **tree){
|
||||
|
||||
AVLTree *right= *tree->rChild;
|
||||
right->lChild = *tree;
|
||||
*tree->rChild = right->lChild;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
void avltree_ll_rotate(AVLTree **tree){
|
||||
|
||||
AVLTree *left = *tree->lChild;
|
||||
left->rChild = *tree;
|
||||
*tree->lChild = left->rChild
|
||||
}
|
||||
|
||||
//
|
||||
void avltree_lr_rotate(AVLTree **tree){
|
||||
|
||||
|
||||
}
|
||||
|
||||
void avltree_rl_rotate(AVLTree **tree){
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void avltree_left_balance(AVLTree **root)
|
||||
{
|
||||
AVLTree *left,*lr;
|
||||
left=(*root)->lChild;
|
||||
switch(left->height)
|
||||
{
|
||||
//检查T的左子树平衡度,并作相应的平衡处理
|
||||
case LH://新节点插入在T的左孩子的左子树上,做单右旋处理
|
||||
(*root)->height=left->height=EH;
|
||||
avltree_ll_rotate(root);
|
||||
break;
|
||||
case RH://新插入节点在T的左孩子的右子树上,做双旋处理
|
||||
lr=left->rChild;
|
||||
switch(lr->height)
|
||||
{
|
||||
case LH:
|
||||
(*root)->height=RH;
|
||||
left->height=EH;
|
||||
break;
|
||||
case EH:
|
||||
(*root)->height=left->height=EH;
|
||||
break;
|
||||
case RH:
|
||||
(*root)->height=EH;
|
||||
left->height=LH;
|
||||
break;
|
||||
}
|
||||
lr->height=EH;
|
||||
L_Rotate(&(*T)->lChild);
|
||||
R_Rotate(T);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void avltree_right_balance(AVLTree **root)
|
||||
{
|
||||
AVLTree right,rl;
|
||||
right=(*root)->rChild;
|
||||
switch(right->height)
|
||||
{
|
||||
case RH://新节点插在T的右孩子的右子树上,要做单左旋处理
|
||||
(*root)->height=right->height=EH;
|
||||
avltree_rr_rotate(root);
|
||||
break;
|
||||
case LH://新节点插在T的右孩子的左子树上,要做双旋处理
|
||||
rl=right->lChild;
|
||||
switch(rl->height)
|
||||
{
|
||||
case LH:
|
||||
(*root)->height=EH;
|
||||
right->height=RH;
|
||||
break;
|
||||
case EH:
|
||||
(*root)->height=right->height=EH;
|
||||
break;
|
||||
case RH:
|
||||
(*root)->height=LH;
|
||||
right->height=EH;
|
||||
break;
|
||||
}
|
||||
rl->height=EH;
|
||||
R_Rotate(&(*root)->rChild);
|
||||
L_Rotate(T);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 插入一个节点key
|
||||
/*
|
||||
|
||||
算法描述:
|
||||
1)如果root为null,则插入一个数据元素为kx 的新结点作为T 的根结点
|
||||
|
||||
2)如果key和root->key相等,不插入
|
||||
|
||||
3)如果key<root->key, 插在root左子树上:
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
AVLTree* avltree_insert(AVLTree* root, KEY_TYPE key){
|
||||
|
||||
|
||||
if (NULL==root)
|
||||
{
|
||||
|
||||
root = (AVLTree *)malloc(sizeof(struct AVLTree));
|
||||
if (!root)
|
||||
{
|
||||
printf("内存分配失败,插入节点失败\n");
|
||||
return root;
|
||||
}
|
||||
root.key = key;
|
||||
root.lChild = NULL;
|
||||
root.rChild = NULL;
|
||||
root.height = 0;
|
||||
}
|
||||
|
||||
|
||||
else if (key=root->key)
|
||||
{
|
||||
printf("节点 %d 已存在 \n", key);
|
||||
}
|
||||
|
||||
else if (key<root->key)//插入左
|
||||
{
|
||||
root->lChild = avltree_insert(root->lChild,key);
|
||||
if (root->lChild->height-root->rChild->height == 2)//不平衡
|
||||
{
|
||||
if (key<root->lChild->key)// LL型
|
||||
{
|
||||
root = avltree_ll_rotate(tree);
|
||||
}
|
||||
|
||||
if (key>root->lChild->key)//LR
|
||||
{
|
||||
root = avltree_lr_rotate(tree);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
else if (key>root->key){
|
||||
|
||||
root->rChild = avltree_insert(root->rChild,key);
|
||||
if (key<root->rChild->key)// RL
|
||||
{
|
||||
root = avltree_rl_rotate(tree);
|
||||
}
|
||||
|
||||
if (key>root->rChild->key)// RR
|
||||
{
|
||||
root = avltree_rr_rotate(tree);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// 删除一个节点
|
||||
AVLTree* avltree_delete(AVLTree* root, KEY_TYPE key){
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 判断是否为AVL树
|
||||
int avltree_isbalance(AVLTree *root){
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 查找
|
||||
AVLTree* avltree_search(AVLTree *root,KEY_TYPE key){
|
||||
|
||||
if (root==NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (root->key == key)
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
else if (root->key>key)
|
||||
{
|
||||
return avltree_search(root->lChild,key);
|
||||
}
|
||||
|
||||
else{
|
||||
|
||||
return avltree_search(root->rChild,key);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 中序遍历
|
||||
void avltree_inorder_traversal(AVLTree* root){
|
||||
|
||||
if (root)
|
||||
{
|
||||
avltree_inorder_traversal(root->lChild);
|
||||
printf(节点值=%d,左右子树的高度差=%d\n,root->key,root->height);
|
||||
avltree_inorder_traversal(root->rChild);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test
|
||||
|
||||
int main(){
|
||||
|
||||
AVLTree *avlTree=NULL;
|
||||
printf("插入节点,创建一个AVL树...\n");
|
||||
|
||||
int values[] = {11,7,222,456,23,8,65,124,88,2,54};
|
||||
for (int i = 0; i < sizeof(values)/sizeof(int); ++i)
|
||||
{
|
||||
printf("插入节点 %d\n", values[i]);
|
||||
avlTree = avltree_insert(avlTree,values[i]);
|
||||
avltree_inorder_traversal(avltree);
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
printf("中序遍历结果:\n");
|
||||
avltree_inorder_traversal(avlTree);
|
||||
|
||||
|
||||
printf("删除一个存在的节点 %d\n", values[1]);
|
||||
avlTree=avltree_delete(avlTree,values[1]);
|
||||
printf("中序遍历结果:\n");
|
||||
avltree_inorder_traversal(avlTree);
|
||||
|
||||
|
||||
printf("删除一个不存在的节点 %d\n",111 );
|
||||
avltree_delete(avlTree,111);
|
||||
printf("中序遍历结果:\n");
|
||||
avltree_inorder_traversal(avlTree);
|
||||
|
||||
printf("查找一个存在的节点 %d\n", values[3]);
|
||||
avltree_search(avltree,values[3]);
|
||||
|
||||
printf("查找一个不存在的节点 %d\n",51);
|
||||
avltree_search(avltree,51);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
### AVL 实现过程中的问题
|
||||
|
||||
|
||||
|
||||
### AVL 实际使用案例
|
||||
|
||||
* LLVM 的 ImmutableSet,其底层的实现选择为 AVL 树
|
||||
* 《一种基于二叉平衡树的P2P覆盖网络的研究》论文
|
||||
@@ -0,0 +1,198 @@
|
||||
|
||||
/*
|
||||
trie树
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ALPHABET_SIZE 26 // 256
|
||||
|
||||
typedef int bool;
|
||||
#define YES 1
|
||||
#define NO 0
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
int count; //count 如果为0,则代表非黄色点,count>0代表是黄色点,同时表示出现次数;
|
||||
char value; //字符
|
||||
struct node * subtries[ALPHABET_SIZE]; //子树
|
||||
} Trie;
|
||||
|
||||
|
||||
|
||||
Trie *trie_create(Trie **trie){
|
||||
|
||||
if (NULL==*trie)
|
||||
{
|
||||
*trie = (Trie *)malloc(sizeof(struct node));
|
||||
if (NULL==*trie)
|
||||
{
|
||||
printf("malloc failure...\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < ALPHABET_SIZE; ++i)
|
||||
{
|
||||
(*trie)->subtries[i]=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return *trie;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//插入字符串(插入一个单词),建立字典树. 返回值 < 0 表示插入失败
|
||||
int trie_insert(Trie *trie,char *c){
|
||||
|
||||
if (trie==NULL || c==NULL) // 对NULL指针解引用会崩溃
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *p = c;
|
||||
Trie *temp = trie;
|
||||
while(*p != '\0'){
|
||||
|
||||
if (temp->subtries==NULL)
|
||||
{
|
||||
//temp->subtries = (struct node *)malloc(sizeof(struct node)*ALPHABET_SIZE);
|
||||
if (temp->subtries==NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (temp->subtries[*p-'a']==NULL)
|
||||
{
|
||||
struct node *newNode = (struct node *)malloc(sizeof(struct node));
|
||||
if (!newNode)
|
||||
{
|
||||
printf("create new node fail \n");
|
||||
return -1;
|
||||
}
|
||||
newNode->value = *p;
|
||||
//newNode->subtries = NULL;
|
||||
temp->subtries[*p-'a'] = newNode;
|
||||
}
|
||||
|
||||
|
||||
temp = temp->subtries[*p-'a'];
|
||||
p++;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 字符串查找,返回值<0表示没有查找到
|
||||
bool trie_query(Trie *trie,char *c){
|
||||
|
||||
if (trie==NULL )
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
char *p = c;
|
||||
Trie *temp = trie;
|
||||
bool ret=NO;
|
||||
if (temp->subtries == NULL)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
while (*p!='\0')
|
||||
{
|
||||
if (temp->subtries[*p-'a']!=NULL && temp->subtries[*p-'a']->value==*p)//匹配
|
||||
{
|
||||
temp = temp->subtries[*p-'a'];
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p =='\0')
|
||||
{
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
void trie_remove(){}
|
||||
|
||||
|
||||
|
||||
|
||||
int main(){
|
||||
|
||||
Trie *trie = NULL;
|
||||
if (!trie_create(&trie))
|
||||
{
|
||||
printf("trie init fail...\n");
|
||||
}
|
||||
|
||||
char *dict[10]={"int","integer","float","char","nonstriater","weibo"};
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (trie_insert(trie,dict[i])<0)
|
||||
{
|
||||
printf("%s 插入失败\n", dict[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 查询cha
|
||||
printf("查询cha \n");
|
||||
if (trie_query(trie,"cha"))
|
||||
{
|
||||
printf("YES\n");
|
||||
}else{
|
||||
printf("NO\n");
|
||||
|
||||
}
|
||||
|
||||
// 查询char
|
||||
printf("查询char \n");
|
||||
if (trie_query(trie,"char"))
|
||||
{
|
||||
printf("YES\n");
|
||||
}else{
|
||||
printf("NO\n");
|
||||
|
||||
}
|
||||
|
||||
// 查询hello
|
||||
printf("查询hello \n");
|
||||
if (trie_query(trie,"hello"))
|
||||
{
|
||||
printf("YES\n");
|
||||
}else{
|
||||
printf("NO\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
|
||||
|
||||
## 字典树trie
|
||||
|
||||
### trie基本
|
||||
|
||||
`字典树`,英文名`Trie树`,Trie一词来自retrieve,发音为/tri:/ “tree”,也有人读为/traɪ/ “try”,
|
||||
又称`单词查找树` 或 `前缀树`,Trie树,是一种树形结构(多叉树)。
|
||||
|
||||
trie,又称为前缀树或字典树,是一种有序树,用于保存关联数组。
|
||||
|
||||
1. 除根节点不包含字符,每个节点都包含一个字符
|
||||
2. 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串
|
||||
3. 每个节点的所有子节点包含的字符都不相同(保证每个节点对应的字符串都不一样)
|
||||
|
||||
比如:
|
||||
|
||||
```
|
||||
/ \
|
||||
/ | \
|
||||
t a i
|
||||
/ \ \
|
||||
o e n
|
||||
/|\ /
|
||||
a d n n
|
||||
```
|
||||
|
||||
上面的Trie树,可以表示字符串集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。
|
||||
|
||||
trie树把每个关键字保存在一条路径上,而不是一个节点中
|
||||
两个有公共前缀的关键字,在Trie树中前缀部分的路径相同,所以Trie树又叫做前缀树(Prefix Tree)。
|
||||
|
||||
|
||||
### trie树存储结构和基本操作
|
||||
|
||||
最简单实现 ---- 26个字母表 a-z (没有考虑数字,大小写,其他字符如=-*/)
|
||||
|
||||
子树用数组存储,浪费空间;如果系统中存在大量字符串,且这些字符串基本没有公共前缀,trie树将消耗大量内存
|
||||
如果用链表存储,查询时需要遍历链表,查询效率有所降低
|
||||
|
||||
```
|
||||
define ALPHABET_NUM 26
|
||||
typedef struct trie_node{
|
||||
char value;
|
||||
bool isKey;/*是否代表一个关键字*/
|
||||
int count; /*可用于词频统计,表示关键字出现的次数*/
|
||||
struct Node *subTries[ALPHABET];
|
||||
}*Trie
|
||||
|
||||
Trie Trie_create();
|
||||
int Trie_insert(Trie trie,char *word); // 插入一个单词
|
||||
int Trie_search(Trie trie,char *word);// 查找一个单词
|
||||
int Trie_delete(Trie trie,char *word);// 删除一个单词
|
||||
|
||||
Trie Trie_create(){
|
||||
trie_node* pNode = new trie_node();
|
||||
pNode->count = 0;
|
||||
for(int i=0; i<ALPHABET_SIZE; ++i)
|
||||
pNode->children[i] = NULL;
|
||||
return pNode;
|
||||
}
|
||||
|
||||
void trie_insert(trie root, char* key)
|
||||
{
|
||||
trie_node* node = root;
|
||||
char* p = key;
|
||||
while(*p)
|
||||
{
|
||||
if(node->children[*p-'a'] == NULL)
|
||||
{
|
||||
node->children[*p-'a'] = create_trie_node();
|
||||
}
|
||||
node = node->children[*p-'a'];
|
||||
++p;
|
||||
}
|
||||
node->count += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询:不存在返回0,存在返回出现的次数
|
||||
*/
|
||||
int trie_search(trie root, char* key)
|
||||
{
|
||||
trie_node* node = root;
|
||||
char* p = key;
|
||||
while(*p && node!=NULL)
|
||||
{
|
||||
node = node->children[*p-'a'];
|
||||
++p;
|
||||
}
|
||||
|
||||
if(node == NULL)
|
||||
return 0;
|
||||
else
|
||||
return node->count;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
trie树的增加和删除都比较麻烦,但索引本身就是写少读多,是否考虑添加删除的复杂度上升,依靠具体场景决定。
|
||||
|
||||
|
||||
### trie 问题
|
||||
|
||||
它的优点是:
|
||||
|
||||
1. 插入和查询的效率很高,都是O(m),其中 m 是待插入/查询的字符串的长度
|
||||
2. Trie树可以对关键字按字典序排序
|
||||
3. 利用字符串的公共前缀来最大限度地减少无谓的字符串比较,提高查询效率
|
||||
|
||||
缺点:
|
||||
|
||||
1. trie 树比较费内存空间,在处理大数据时会内存吃紧
|
||||
2. 当hash函数较好时,Hash查询效率比 trie 更优
|
||||
|
||||
[知乎这里](http://www.zhihu.com/question/27168319)有个问题:`10万个串找给定的串是否存在`, 对trie和hash两种方案给出了讨论。
|
||||
|
||||
|
||||
[DATrie](https://github.com/kmike/datrie) 是使用python实现的双数组trie树, 双数组可以减少内存的使用量 。有关 double-array trie,可以参考[这篇论文](http://linux.thai.net/~thep/datrie/datrie.html)
|
||||
|
||||
|
||||
### trie应用
|
||||
|
||||
典型应用是:前缀查询,字符串查询,排序
|
||||
|
||||
* 用于统计,排序和保存大量的字符串(但不仅限于字符串)
|
||||
* 经常被搜索引擎系统用于文本词频统计
|
||||
* 排序大量字符串
|
||||
* 用于索引结构
|
||||
* 敏感词过滤
|
||||
|
||||
### 实际应用问题
|
||||
|
||||
1. 给你100000个长度不超过10的单词。对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位置
|
||||
分析思路一:trie树 ,找到这个字符串查询操作就可以了,如何知道出现的第一个位置呢?我们可以在trie树中加一个字段来记录当前字符串第一次出现的位置。
|
||||
|
||||
2. 已知n个由小写字母构成的平均长度为10的单词,判断其中是否存在某个串为另一个串的前缀子串
|
||||
|
||||
3. 给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
|
||||
分析:trie树查询单词的应用。先建立N个熟词的前缀树,然后按文章的单词一次查询。
|
||||
|
||||
4. 给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。
|
||||
分析:先用不良单词建立trie树,然后过滤文本(每个单词都在trie树上查询,查询的复杂度O(1),效率非常高),这正是`敏感词过滤系统(或垃圾评论系统)`的原理。
|
||||
|
||||
5. 给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出
|
||||
分析:这是trie树排序的典型应用,建立N个单词的trie树,然后线序遍历整个树,就可以达到效果。
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
## 伸展树 (splay tree)
|
||||
|
||||
伸展树是一种自平衡的二叉排序树。为什么需要这些自平衡的二叉排序树?
|
||||
|
||||
n个节点的完全二叉树,其查找,删除的复杂度都是O(logN),但是如果频繁的插入删除,导致二叉树退化成一个n个节点的单链表,也就是`插入,查找复杂度趋于O(N)`,为了克服这个缺点,出现了很多二叉查找树的变形,如AVL树,红黑树,以及接下来介绍的 伸展树(splay tree)。
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
## 后缀树(suffix tree)
|
||||
|
||||
###后缀树的应用
|
||||
|
||||
可以解决很多字符串的问题
|
||||
|
||||
1. 查找字符串S1是否在字符串S中
|
||||
2. 指定字符串S1在字符串S中出现的次数
|
||||
3. 字符串S中的最长重复子串
|
||||
4. 2个字符串的最长公共部分
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
## B树
|
||||
|
||||
平衡查找树,一种多路查找树。
|
||||
|
||||
能保证数据插入和删除情况下,任然保持执行效率。
|
||||
|
||||
一个M阶的B树满足:
|
||||
|
||||
1. 每个节点最多M个子节点
|
||||
2. 除跟节点和叶节点外,其它每个节点至少有M/2个孩子
|
||||
3. 根节点至少2个节点
|
||||
4. 所有叶节点在同一层,叶节点不包含任何关键字信息
|
||||
5. 有k个关键字的页节点包含k+1个孩子
|
||||
|
||||
也就是说:`根节点到每个叶节点的路径长度都是相同的。`
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
|
||||
|
||||
void heap_build(int *a,int length){
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 插入
|
||||
void heap_insert(int *a,int v){
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//删除,删除的元素放在value指向的内存中
|
||||
void heap_delete(int *a,int *value){
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
## 树🌲
|
||||
|
||||
介绍树相关的算法
|
||||
|
||||
* 二叉查找树
|
||||
* 赫夫曼编码 Huffman
|
||||
* 字典树trie(前缀树,单词查找树)
|
||||
* 伸展树
|
||||
* 后缀树
|
||||
* AVL树
|
||||
* 红黑树
|
||||
* B树
|
||||
* B+树 mysql索引使用B+树的数据结构
|
||||
* B*树
|
||||
* R树
|
||||
* Treap 树
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
## 赫夫曼编码 Huffman
|
||||
|
||||
这是一个经典的压缩算法。通过`字符出现的频率`,`优先级`,`二叉树`进行的压缩算法。
|
||||
|
||||
对一个字符串,计算每个字符出现的次数,把这些字符放到优先队列(priority queue)
|
||||
这这个priority queue转出二叉树
|
||||
|
||||
需要一个字符编码表来解码,通过二叉树建立huffman编码和解码的字典表
|
||||
|
||||
|
||||
原始串:
|
||||
二级制编码:
|
||||
huffman编码:
|
||||
|
||||
|
||||
### 存储结构和基本操作
|
||||
|
||||
```
|
||||
struct node{
|
||||
char *huffCode; // 叶子节点的huff编码
|
||||
int weight;
|
||||
struct node *left,right;
|
||||
}
|
||||
```
|
||||
|
||||
### 构建赫夫曼树
|
||||
|
||||
原则:出现频率越多的会在越上层,编码也越短,出现频率越少的在越下层,编码也越长。
|
||||
不存在某一个编码是另一个编码的前缀,字符都在叶节点上,所以不会存在一个编码是另一个编码的前缀
|
||||
二叉树每个节点要么是叶子节点,要么是双分支节点(且左分支编码为0,右分支编码为1)
|
||||
|
||||
|
||||
### 压缩
|
||||
|
||||
1. 扫描输入文件,统计各个字符出现的次数,对结构排序 (hash统计每个字符出现的次数)
|
||||
2. 根据排序结构,构建赫夫曼树 (贪心策略,每次选频率值最低的2个节点合并,需要优先队列帮组(priority queue,又叫最小堆))
|
||||
3. 对树进行遍历(左分支编码为0,右分支编码为1),得到各个字符的huffman编码,存到hash表中(这个就是编解码表,也可直接存储到节点中,如上面的char *huffCode)
|
||||
4. 重新对文件扫描,根据hash表进行压缩
|
||||
|
||||
压缩的文件为了能够解压缩,需要一个文件头,用来重建赫夫曼树,包括:
|
||||
被编码的文本长度 unsigned int size
|
||||
字符频率表 unsigned char freqs[NUM_CHARS]
|
||||
|
||||
###解压缩
|
||||
|
||||
1. 读取文件头
|
||||
2. 遍历编码后的bits,从赫夫曼树的根节点出发,遇到0,进入左子树,遇到1进入右子树,直到叶节点
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
|
||||
## 二叉查找树
|
||||
|
||||
二叉查找树(Binary search tree),也叫`有序二叉树(Ordered binary tree)`,`排序二叉树(Sorted binary tree)`。是指一个空树或者具有下列性质的二叉树:
|
||||
|
||||
1. 若任意节点的左子树不为空,则左子树上所有的节点值小于它的根节点值
|
||||
2. 若任意节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值
|
||||
3. 任意节点左右子树也为二叉查找树
|
||||
4. 没有键值相等的节点
|
||||
|
||||
```
|
||||
typedef int ElemType;
|
||||
typedef struct BiSearchTree{
|
||||
ElemType key;
|
||||
struct BiSearchTree *lChild;
|
||||
struct BiSearchTree *rChild;
|
||||
}BiSearchTree;
|
||||
BiSearchTree *bisearch_tree_insert(BiSearchTree *tree,ElemType node);
|
||||
int bisearch_tree_delete(BiSearchTree **tree,ElemType node);
|
||||
int bisearch_tree_search(BiSearchTree *tree,ElemType node);
|
||||
```
|
||||
|
||||
|
||||
删除节点,需要重建排序树
|
||||
|
||||
1) 删除节点是叶子节点(分支为0),结构不破坏
|
||||
2)删除节点只有一个分支(分支为1),结构也不破坏
|
||||
3)删除节点有2个分支,此时删除节点
|
||||
|
||||
思路一: 选左子树的最大节点,或右子树最小节点替换
|
||||
|
||||
```
|
||||
int bisearch_tree_delete(BiSearchTree **tree,ElemType node){
|
||||
|
||||
if (NULL==tree) {
|
||||
return -1;
|
||||
}
|
||||
// 查找删除目标节点
|
||||
BiSearchTree *target=*tree,*parent=NULL;
|
||||
while (NULL!=target) {
|
||||
if (node<target->key) {
|
||||
parent=target;
|
||||
target=target->lChild;
|
||||
}else if(node==target->key){
|
||||
break;
|
||||
}else{
|
||||
parent=target;
|
||||
target=target->rChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL==target) {
|
||||
printf("树为空,或想要删除的节点不存在\n");
|
||||
return -1;
|
||||
}
|
||||
//该节点为叶子节点,直接删除
|
||||
if (!target->rChild && !target->lChild)
|
||||
{
|
||||
if (NULL==parent) {////只有一个节点的二叉查找树
|
||||
*tree=NULL;
|
||||
}else{
|
||||
if (target->key>parent->key) {
|
||||
parent->rChild=NULL;
|
||||
}else{
|
||||
parent->lChild=NULL;
|
||||
}
|
||||
|
||||
}
|
||||
free(target);//父节点处理,不然野指针,造成崩溃
|
||||
}
|
||||
|
||||
else if(!target->rChild){ //右子树空则只需重接它的左子树,用左子树替换掉当前要删除的节点
|
||||
BiSearchTree *del=target->lChild;
|
||||
target->key = target->lChild->key;
|
||||
target->lChild=target->lChild->lChild;
|
||||
target->rChild=target->lChild->rChild;
|
||||
|
||||
free(del);
|
||||
}
|
||||
else if(!target->lChild){ //左子树空只需重接它的右子树
|
||||
BiSearchTree *del=target->rChild;
|
||||
target->key = target->rChild->key;
|
||||
target->lChild=target->rChild->lChild;
|
||||
target->rChild=target->rChild->rChild;
|
||||
|
||||
free(del);
|
||||
}
|
||||
else{ //左右子树均不空,p,t 2个指针一前以后,将左子树最大的节点(肯定是一个最右的节点)替换到删除的节点后,还需要处理左子树最大节点的左子树
|
||||
|
||||
BiSearchTree *p=target,*t=target->lChild;
|
||||
while (t->rChild) {
|
||||
p = t;
|
||||
t=t->rChild;
|
||||
}// 找到左子树最大的,是删除节点的直接“前驱”
|
||||
|
||||
target->key = t->key;
|
||||
|
||||
if (p!=target) {
|
||||
p->rChild = t->lChild;
|
||||
}else{
|
||||
target->lChild = t->lChild;
|
||||
}
|
||||
|
||||
free(t);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user