www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 嵌入式微處理器
[導讀]時間、空間復雜度比較 查找算法 平均時間復雜度 空間復雜度 查找條件 順序查找 O(n) O(1) 無序或有序 二分查找(折半查找) O(log2n) O(1) 有序 插值查找 O(log2(log2n)) O(1) 有序 斐波那契查找 O(log2n) O(1) 有序 哈希查找 O(1) O(n) 無序或有序 二叉查找


時間、空間復雜度比較

查找算法 平均時間復雜度 空間復雜度 查找條件
順序查找 O(n) O(1) 無序或有序
二分查找(折半查找) O(log2n) O(1) 有序
插值查找 O(log2(log2n)) O(1) 有序
斐波那契查找 O(log2n) O(1) 有序
哈希查找 O(1) O(n) 無序或有序
二叉查找樹(二叉搜索樹查找) O(log2n)

紅黑樹 O(log2n)

2-3樹 O(log2n - log3n)

B樹/B+樹 O(log2n)

1 順序查找

算法思路

對于任意一個序列,從一端開始,順序掃描,依次將掃描到的結點關鍵字與給定值k相比較,若相等則表示查找成功;若掃描結束仍沒有找到關鍵字等于k的結點,表示查找失敗。

代碼

#include <stdio.h>
#include <stdlib.h>
#define keyType int
//2020.05.24
typedef struct
{
keyType key;//查找表中每個數(shù)據(jù)元素的值
}ElemType;

typedef struct
{
ElemType *elem;//存放查找表中數(shù)據(jù)元素的數(shù)組
int length;//記錄查找表中數(shù)據(jù)的總數(shù)量
}SSTable;

//創(chuàng)建查詢數(shù)據(jù)
void Create(SSTable **st,int length)
{
(*st)=(SSTable*)malloc(sizeof(SSTable));
(*st)->length=length;
(*st)->elem =(ElemType*)malloc((length+1)*sizeof(ElemType));
printf("輸入表中的數(shù)據(jù)元素:\n");
//根據(jù)查找表中數(shù)據(jù)元素的總長度,在存儲時,從數(shù)組下標為 1 的空間開始存儲數(shù)據(jù)
for (int i=1; i<=length; i++)
{
scanf("%d",&((*st)->elem[i].key));
}
}

//順序查找函數(shù),其中key為要查找的元素
int Search_seq(SSTable *str,keyType key)
{
//str->elem[0].key=key;//將關鍵字作為一個數(shù)據(jù)元素存放到查找表的第一個位置,起監(jiān)視哨的作用
int len = str->length;
//從最后一個數(shù)據(jù)元素依次遍歷,一直遍歷到數(shù)組下標為0
for(int i=1; i<len+1; i++) //創(chuàng)建數(shù)據(jù)從數(shù)組下標為1開始,查詢也從1開始
{
if(str->elem[i].key == key)
{
return i;
}
}
//如果 i=0,說明查找失?。徊檎页晒Ψ祷匾檎以豮ey的位置i
return 0;
}

int main()
{
SSTable *str;
int num;
printf("請輸入創(chuàng)建數(shù)據(jù)元素的個數(shù):\n");
scanf("%d",&num);
Create(&str, num);
getchar();
printf("請輸入要查找的數(shù)據(jù):\n");
int key;
scanf("%d",&key);
int location=Search_seq(str, key);
if (location==0) {
printf("查找失敗");
}else{
printf("要查找的%d的順序為:%d",key,location);
}
return 0;
}

運行結果

查找成功
查找失敗

2 二分查找(折半查找)

算法思路

  1. 確定查找范圍low=0,high=N-1,計算中項mid=(low+high)/2。

  2. 若mid==x或low>=high,則結束查找;否則,向下繼續(xù)。

  3. 若amid<x,說明待查找的元素值只可能在比中項元素大的范圍內,則把mid+1的值賦給low,并重新計算mid,轉去執(zhí)行步驟2;若mid>x,說明待查找的元素值只可能在比中項元素小的范圍內,則把mid-1的值賦給higt,并重新計算mid,轉去執(zhí)行步驟2。

說明

  • 查找元素必須是有序的,如果是無序的則要先進行排序操作。

  • 在做查找的過程中,如果 low 指針和 high 指針的中間位置在計算時位于兩個關鍵字中間,即求得 mid 的位置不是整數(shù),需要統(tǒng)一做取整操作。

折半查找的前提條件是需要有序表順序存儲,對于靜態(tài)查找表,一次排序后不再變化,折半查找能得到不錯的效率。但對于需要頻繁執(zhí)行插入或刪除操作的數(shù)據(jù)集來說,維護有序的排序會帶來不小的工作量,那就不建議使用。

                         ——《大話數(shù)據(jù)結構》

代碼

#include <stdio.h>
#include <stdlib.h>
#define keyType int
typedef struct
{
keyType key;//查找表中每個數(shù)據(jù)元素的值
}ElemType;

typedef struct
{
ElemType *elem;//存放查找表中數(shù)據(jù)元素的數(shù)組
int length;//記錄查找表中數(shù)據(jù)的總數(shù)量
}SSTable;

//創(chuàng)建查詢數(shù)據(jù)
void Create(SSTable **st,int length)
{
(*st)=(SSTable*)malloc(sizeof(SSTable));
(*st)->length=length;
(*st)->elem =(ElemType*)malloc((length+1)*sizeof(ElemType));
printf("輸入表中的數(shù)據(jù)元素:\n");
//根據(jù)查找表中數(shù)據(jù)元素的總長度,在存儲時,從數(shù)組下標為 1 的空間開始存儲數(shù)據(jù)
for (int i=1; i<=length; i++)
{
scanf("%d",&((*st)->elem[i].key));
}
}

//折半查找函數(shù) key為要查找的元素
int Search_Bin(SSTable *str,keyType key)
{
int low=1;//初始狀態(tài) low 指針指向第一個關鍵字
int high=str->length;//high 指向最后一個關鍵字
int mid;
while (low<=high)
{
mid=(low+high)/2;//int 本身為整形,所以,mid 每次為取整的整數(shù)
if(str->elem[mid].key==key)//如果 mid 指向的同要查找的相等,返回 mid 所指向的位置
{
return mid;
}
else if(str->elem[mid].key>key)//如果mid指向的關鍵字較大,則更新 high 指針的位置
{
high=mid-1;
}
//反之,則更新 low 指針的位置
else
{
low=mid+1;
}
}
return 0;
}

int main()
{
SSTable *str;
int num;
printf("請輸入創(chuàng)建數(shù)據(jù)元素的個數(shù):\n");
scanf("%d",&num);
Create(&str, num);
getchar();
printf("請輸入要查找的數(shù)據(jù):\n");
int key;
scanf("%d",&key);
int location=Search_Bin(str, key);
if (location==0) {
printf("沒有查找到");
}else{
printf("要查找的%d的順序為:%d",key,location);
}
return 0;
}

運行結果

查找成功
沒有查找到

3 插值查找

插值查找基于二分查找算法,將查找點的選擇改進為自適應選擇,可以提高查找效率。當然,差值查找也屬于有序查找

算法思路

  1. 確定查找范圍low=0,high=N-1,計算中項mid=low+(key-a[low])/(a[high]-a[low])*(high-low)。

  2. 若mid==x或low>=high,則結束查找;否則,向下繼續(xù)。

  3. 若amid<x,說明待查找的元素值只可能在比中項元素大的范圍內,則把mid+1的值賦給low,并重新計算mid,轉去執(zhí)行步驟2;若mid>x,說明待查找的元素值只可能在比中項元素小的范圍內,則把mid-1的值賦給higt,并重新計算mid,轉去執(zhí)行步驟2。

說明

  • 插值查找是基于折半查找進行了優(yōu)化的查找方法。
  • 當表長較大,而關鍵字分布又比較均勻的查找表來說,插值查找算法的平均性能要比折半查找要好得多。

代碼

#include<stdio.h>

int array[10] = { 1, 4, 9, 16, 27, 31, 33, 35, 45, 64 };

int InsertionSearch(int data)
{
int low = 0;
int high = 10 - 1;
int mid = -1;
int comparisons = 1;
int index = -1;

while(low <= high)
{
printf("比較 %d 次\n" , comparisons );
printf("low : %d, list[%d] = %d\n", low, low, array[low]);
printf("high : %d, list[%d] = %d\n", high, high, array[high]);

comparisons++;
mid = low + (((double)(high - low) / (array[high] - array[low])) * (data - array[low]));
printf("mid = %d\n",mid);

// 沒有找到
if(array[mid] == data)
{
index = mid;
break;
}
else
{
if(array[mid] < data)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
}

printf("比較次數(shù): %d\n", --comparisons);
return index;
}

int main()
{
int location = InsertionSearch(27); //測試代,查27,可以找到
if(location != -1)
{
printf("查找元素順序為: %d\n" ,(location+1));
}
else
{
printf("沒有查找到");
}
return 0;
}

運行結果

運行結果

4 斐波那契查找

斐波那契查找與折半查找很相似,他是根據(jù)斐波那契序列的特點對有序表進行分割的。他要求開始表中記錄的個數(shù)為某個斐波那契數(shù)小1,及n=F(k)-1;開始將k值與第F(k-1)位置的記錄進行比較(及mid=low+F(k-1)-1).

算法思路

  1. 相等,mid位置的元素即為所求

  2. 大于,low=mid+1,k-=2;

  3. 小于,high=mid-1,k-=1。

說明

low=mid+1說明待查找的元素在[mid+1,high]范圍內,k-=2 說明范圍[mid+1,high]內的元素個數(shù)為n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1個,所以可以遞歸的應用斐波那契查找。

代碼

#include "stdafx.h"
#include <memory>
#include <iostream>
using namespace std;

const int max_size=20;//斐波那契數(shù)組的長度

/*構造一個斐波那契數(shù)組*/
void Fibonacci(int * F)
{
F[0]=0;
F[1]=1;
for(int i=2;i<max_size;++i)
F[i]=F[i-1]+F[i-2];
}

/*定義斐波那契查找法*/
int FibonacciSearch(int *a, int n, int key) //a為要查找的數(shù)組,n為要查找的數(shù)組長度,key為要查找的關鍵字
{
int low=0;
int high=n-1;

int F[max_size];
Fibonacci(F);//構造一個斐波那契數(shù)組F

int k=0;
while(n>F[k]-1)//計算n位于斐波那契數(shù)列的位置
++k;

int * temp;//將數(shù)組a擴展到F[k]-1的長度
temp=new int [F[k]-1];
memcpy(temp,a,n*sizeof(int));

for(int i=n;i<F[k]-1;++i)
temp[i]=a[n-1];

while(low<=high)
{
int mid=low+F[k-1]-1;
if(key<temp[mid])
{
high=mid-1;
k-=1;
}
else if(key>temp[mid])
{
low=mid+1;
k-=2;
}
else
{
if(mid<n)
return mid; //若相等則說明mid即為查找到的位置
else
return n-1; //若mid>=n則說明是擴展的數(shù)值,返回n-1
}
}
delete [] temp;
return 0;
}

int main()
{
int a[] = {0,1,4,35,47,53,62,78,88,99};
int key=47;
int index=FibonacciSearch(a,sizeof(a)/sizeof(int),key);
if(index == 0)
{
cout<<"沒有找到"<<key;
}
else
{
cout<<key<<" 的位置是:"<<index;
}
return 0;
}

運行結果

47的位置為5

5 哈希查找

哈希表

我們使用一個下標范圍比較大的數(shù)組來存儲元素??梢栽O計一個函數(shù)(哈希函數(shù), 也叫做散列函數(shù)),使得每個元素的關鍵字都與一個函數(shù)值(即數(shù)組下標)相對應,于是用這個數(shù)組單元來存儲這個元素;也可以簡單的理解為,按照關鍵字為每一個元素"分類",然后將這個元素存儲在相應"類"所對應的地方。但是,不能夠保證每個元素的關鍵字與函數(shù)值是一一對應的,因此極有可能出現(xiàn)對于不同的元素,卻計算出了相同的函數(shù)值,這樣就產生了"沖突",換句話說,就是把不同的元素分在了相同的"類"之中。后面我們將看到一種解決"沖突"的簡便做法。

"直接定址"與"解決沖突"是哈希表的兩大特點。

哈希函數(shù)

規(guī)則:通過某種轉換關系,使關鍵字適度的分散到指定大小的的順序結構中,越分散,則以后查找的時間復雜度越小,空間復雜度越高。

算法思路

如果所有的鍵都是整數(shù),那么就可以使用一個簡單的無序數(shù)組來實現(xiàn):將鍵作為索引,值即為其對應的值,這樣就可以快速訪問任意鍵的值。這是對于簡單的鍵的情況,我們將其擴展到可以處理更加復雜的類型的鍵。

  1. 用給定的哈希函數(shù)構造哈希表;
  2. 根據(jù)選擇的沖突處理方法(常見方法:拉鏈法和線性探測法)解決地址沖突;
  3. 在哈希表的基礎上執(zhí)行哈希查找;

代碼

#include<stdio.h>
#include<stdlib.h>

#define SUCCESS 1
#define UNSUCCESS 0
#define OVERFLOW -1
#define OK 1
#define ERROR -1
#define MAXNUM 9999 // 用于初始化哈希表的記錄 key

typedef int Status;
typedef int KeyType;

// 哈希表中的記錄類型
typedef struct
{
KeyType key;
}RcdType;

// 哈希表類型
typedef struct
{
RcdType *rcd;
int size;
int count;
int *tag;
}HashTable;

// 哈希表每次重建增長后的大小
int hashsize[] = { 11, 31, 61, 127, 251, 503 };
int index = 0;

// 初始哈希表
Status InitHashTable(HashTable &H, int size)
{
int i;
H.rcd = (RcdType *)malloc(sizeof(RcdType)*size);
H.tag = (int *)malloc(sizeof(int)*size);
if (NULL == H.rcd || NULL == H.tag) return OVERFLOW;
KeyType maxNum = MAXNUM;
for (i = 0; i < size; i++)
{
H.tag[i] = 0;
H.rcd[i].key = maxNum;
}
H.size = size;
H.count = 0;
return OK;
}

// 哈希函數(shù):除留余數(shù)法
int Hash(KeyType key, int m)
{
return (3 * key) % m;
}

// 處理哈希沖突:線性探測
void collision(int &p, int m)
{
p = (p + 1) % m;
}

// 在哈希表中查詢
Status SearchHash(HashTable H, KeyType key, int &p, int &c)
{
p = Hash(key, H.size);
int h = p;
c = 0;
while ((1 == H.tag[p] && H.rcd[p].key != key) || -1 == H.tag[p])
{
collision(p, H.size); c++;
}

if (1 == H.tag[p] && key == H.rcd[p].key) return SUCCESS;
else return UNSUCCESS;
}

//打印哈希表
void printHash(HashTable H)
{
int i;
printf("key : ");
for (i = 0; i < H.size; i++)
printf("%3d ", H.rcd[i].key);
printf("\n");
printf("tag : ");
for (i = 0; i < H.size; i++)
printf("%3d ", H.tag[i]);
printf("\n\n");
}

// 函數(shù)聲明:插入哈希表
Status InsertHash(HashTable &H, KeyType key);

// 重建哈希表
Status recreateHash(HashTable &H)
{
RcdType *orcd;
int *otag, osize, i;
orcd = H.rcd;
otag = H.tag;
osize = H.size;

InitHashTable(H, hashsize[index++]);
//把所有元素,按照新哈希函數(shù)放到新表中
for (i = 0; i < osize; i++)
{
if (1 == otag[i])
{
InsertHash(H, orcd[i].key);
}
}
return OK;
}

// 插入哈希表
Status InsertHash(HashTable &H, KeyType key)
{
int p, c;
if (UNSUCCESS == SearchHash(H, key, p, c))
{ //沒有相同key
if (c*1.0 / H.size < 0.5)
{ //沖突次數(shù)未達到上線
//插入代碼
H.rcd[p].key = key;
H.tag[p] = 1;
H.count++;
return SUCCESS;
}
else recreateHash(H); //重構哈希表
}
return UNSUCCESS;
}

// 刪除哈希表
Status DeleteHash(HashTable &H, KeyType key)
{
int p, c;
if (SUCCESS == SearchHash(H, key, p, c))
{
//刪除代碼
H.tag[p] = -1;
H.count--;
return SUCCESS;
}
else return UNSUCCESS;
}

int main()
{
printf("-----哈希表-----\n");
HashTable H;
int i;
int size = 11;
KeyType array[8] = { 22, 41, 53, 46, 30, 13, 12, 67 };
KeyType key;

//初始化哈希表
printf("初始化哈希表\n");
if (SUCCESS == InitHashTable(H, hashsize[index++])) printf("初始化成功\n");

//插入哈希表
printf("插入哈希表\n");
for (i = 0; i <= 7; i++)
{
key = array[i];
InsertHash(H, key);
printHash(H);
}

//刪除哈希表
printf("刪除哈希表中key為12的元素\n");
int p, c;
if (SUCCESS == DeleteHash(H, 12))
{
printf("刪除成功,此時哈希表為:\n");
printHash(H);
}

//查詢哈希表
printf("查詢哈希表中key為67的元素\n");
if (SUCCESS == SearchHash(H, 67, p, c)) printf("查詢成功\n");

//再次插入,測試哈希表的重建
printf("再次插入,測試哈希表的重建:\n");
KeyType array1[8] = { 27, 47, 57, 47, 37, 17, 93, 67 };
for (i = 0; i <= 7; i++)
{
key = array1[i];
InsertHash(H, key);
printHash(H);
}

getchar();
return 0;
}

6 二叉樹查找

二叉查找樹是先對待查找的數(shù)據(jù)進行生成樹,確保樹的左分支的值小于右分支的值,然后在就行和每個節(jié)點的父節(jié)點比較大小,查找最適合的范圍。這個算法的查找效率很高,但是如果使用這種查找方法要首先創(chuàng)建樹。

算法思路

  1. 若b是空樹,則搜索失?。?
  2. 若x等于b的根節(jié)點的數(shù)據(jù)域之值,則查找成功:
  3. 若x小于b的根節(jié)點的數(shù)據(jù)域之值,則搜索左子樹:
  4. 查找右子樹。

代碼

遞歸和非遞歸

//非遞歸查找算法
BSTNode *BST_Search(BiTree T,ElemType key,BSTNode *&p)
{
//查找函數(shù)返回指向關鍵字值為key的結點指針,不存在則返回NULL
p=NULL;
while(T!=NULL&&key!=T->data)
{
p=T; //指向被查找結點的雙親
if(key<T->data)
T=T->lchild; //查找左子樹
else
T=T->rchild; //查找右子樹
}
return T;
}

//遞歸算法
Status Search_BST(BiTree T, int key, BiTree f, BiTree *p)
{
//查找BST中是否存在key,f指向T雙親,其初始值為NULL
//若查找成功,指針p指向數(shù)據(jù)元素結點,返回true;
//若失敗,p指向查找路徑上訪問的最后一個結點并返回false
if(!T)
{
*p=f;
return false;
}
else if(key==T->data)
{ //查找成功
*p=T;
return true;
}
else if(key<T->data)
return Search_BST(T->lchild,key,T,p); //遞歸查找左子樹
else
return Search_BST(T->rchild,key,T,p); //遞歸查找右子樹

}

7 2-3樹

2-3樹運行每個節(jié)點保存1個或者兩個的值。對于普通的2節(jié)點(2-node),他保存1個key和左右兩個自己點。對應3節(jié)點(3-node),保存兩個Key,2-3查找樹的定義如下:

  1. 要么為空,要么:

  2. 對于2節(jié)點,該節(jié)點保存一個key及對應value,以及兩個指向左右節(jié)點的節(jié)點,左節(jié)點也是一個2-3節(jié)點,所有的值都比key要小,右節(jié)點也是一個2-3節(jié)點,所有的值比key要大。

  3. 對于3節(jié)點,該節(jié)點保存兩個key及對應value,以及三個指向左中右的節(jié)點。左節(jié)點也是一個2-3節(jié)點,所有的值均比兩個key中的最小的key還要小;中間節(jié)點也是一個2-3節(jié)點,中間節(jié)點的key值在兩個跟節(jié)點key值之間;右節(jié)點也是一個2-3節(jié)點,節(jié)點的所有key值比兩個key中的最大的key還要大。

算法思路:

要判斷一個鍵是否在樹中,我們先將它和根結點中的鍵比較。如果它和其中的任何一個相等,查找命中。否則我們就根據(jù)比較的結果找到指向相應區(qū)間的鏈接,并在其指向的子樹中遞歸地繼續(xù)查找。如果這是個空鏈接,查找未命中。

2-3 樹中查找鍵為H的節(jié)點
2-3 樹中查找鍵為B的節(jié)點

代碼

two_three *search23(two_three *t, element x)
{
while(t)
{
if (x < t->data_l)
{
t = t->left_child;
}
else if (x > t->data_l && x < t->data_r)
{
t = t->middle_child;
}
else if (x > t->data_r)
{
t = t->right_child;
}
else
{
return t;
}
}
return NULL;
}

8 紅黑樹

2-3查找樹能保證在插入元素之后能保持樹的平衡狀態(tài),最壞情況下即所有的子節(jié)點都是2-node,樹的高度為lgn,從而保證了最壞情況下的時間復雜度。但是2-3樹實現(xiàn)起來比較復雜,于是就有了一種簡單實現(xiàn)2-3樹的數(shù)據(jù)結構,即紅黑樹(Red-Black Tree)。

理解紅黑樹一句話就夠了:紅黑樹就是用紅鏈接表示3-結點的2-3樹

現(xiàn)在我們對2-3樹進行改造,改造成一個二叉樹。怎么改造呢?對于2節(jié)點,保持不變;對于3節(jié)點,我們首先將3節(jié)點中左側的元素標記為紅色,然后我們將其改造成圖3的形式;

再將3節(jié)點的位于中間的子節(jié)點的父節(jié)點設置為父節(jié)點中那個紅色的節(jié)點,如圖4的所示;最后我們將圖4的形式改為二叉樹的樣子,如圖5所示。圖5是不是很熟悉,沒錯,這就是我們常常提到的大名鼎鼎的紅黑樹了。如下圖所示。

2-3樹轉紅黑樹

為什么使用紅黑樹

  • 紅黑樹是一種平衡樹,他復雜的定義和規(guī)則都是為了保證樹的平衡性。如果樹不保證他的平衡性就是下圖:很顯然這就變成一個鏈表了。
  • 保證平衡性的最大的目的就是降低樹的高度,因為樹的查找性能取決于樹的高度。所以樹的高度越低搜索的效率越高!
  • 這也是為什么存在二叉樹、搜索二叉樹等,各類樹的目的。

紅黑樹性質

  • 每個節(jié)點要么是黑色,要么是紅色。
  • 根節(jié)點是黑色。
  • 每個葉子節(jié)點(NIL)是黑色。
  • 每個紅色結點的兩個子結點一定都是黑色。
  • 任意一結點到每個葉子結點的路徑都包含數(shù)量相同的黑結點。

算法思路

紅黑樹的思想就是對2-3查找樹進行編碼,尤其是對2-3查找樹中的3-nodes節(jié)點添加額外的信息。紅黑樹中將節(jié)點之間的鏈接分為兩種不同類型,紅色鏈接,他用來鏈接兩個2-nodes節(jié)點來表示一個3-nodes節(jié)點。黑色鏈接用來鏈接普通的2-3節(jié)點。特別的,使用紅色鏈接的兩個2-nodes來表示一個3-nodes節(jié)點,并且向左傾斜,即一個2-node是另一個2-node的左子節(jié)點。這種做法的好處是查找的時候不用做任何修改,和普通的二叉查找樹相同

代碼

#define BLACK 1
#define RED 0
#include <iostream>

using namespace std;

class bst
{
private:

struct Node
{
int value;
bool color;
Node *leftTree, *rightTree, *parent;

Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { }

Node* grandparent()
{
if (parent == NULL)
{
return NULL;
}
return parent->parent;
}

Node* uncle()
{
if (grandparent() == NULL)
{
return NULL;
}
if (parent == grandparent()->rightTree)
return grandparent()->leftTree;
else
return grandparent()->rightTree;
}

Node* sibling()
{
if (parent->leftTree == this)
return parent->rightTree;
else
return parent->leftTree;
}
};

void rotate_right(Node *p)
{
Node *gp = p->grandparent();
Node *fa = p->parent;
Node *y = p->rightTree;

fa->leftTree = y;

if (y != NIL)
y->parent = fa;
p->rightTree = fa;
fa->parent = p;

if (root == fa)
root = p;
p->parent = gp;

if (gp != NULL)
{
if (gp->leftTree == fa)
gp->leftTree = p;
else
gp->rightTree = p;
}

}

void rotate_left(Node *p)
{
if (p->parent == NULL)
{
root = p;
return;
}
Node *gp = p->grandparent();
Node *fa = p->parent;
Node *y = p->leftTree;

fa->rightTree = y;

if (y != NIL)
y->parent = fa;
p->leftTree = fa;
fa->parent = p;

if (root == fa)
root = p;
p->parent = gp;

if (gp != NULL)
{
if (gp->leftTree == fa)
gp->leftTree = p;
else
gp->rightTree = p;
}
}

void inorder(Node *p)
{
if (p == NIL)
return;

if (p->leftTree)
inorder(p->leftTree);

cout << p->value << " ";

if (p->rightTree)
inorder(p->rightTree);
}

string outputColor(bool color)
{
return color ? "BLACK" : "RED";
}

Node* getSmallestChild(Node *p)
{
if (p->leftTree == NIL)
return p;
return getSmallestChild(p->leftTree);
}

bool delete_child(Node *p, int data)
{
if (p->value > data)
{
if (p->leftTree == NIL)
{
return false;
}
return delete_child(p->leftTree, data);
}
else if (p->value < data)
{
if (p->rightTree == NIL)
{
return false;
}
return delete_child(p->rightTree, data);
}
else if (p->value == data)
{
if (p->rightTree == NIL)
{
delete_one_child(p);
return true;
}
Node *smallest = getSmallestChild(p->rightTree);
swap(p->value, smallest->value);
delete_one_child(smallest);

return true;
}
else
{
return false;
}
}

void delete_one_child(Node *p)
{
Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree;
if (p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL)
{
p = NULL;
root = p;
return;
}

if (p->parent == NULL)
{
delete p;
child->parent = NULL;
root = child;
root->color = BLACK;
return;
}

if (p->parent->leftTree == p)
{
p->parent->leftTree = child;
}
else
{
p->parent->rightTree = child;
}
child->parent = p->parent;

if (p->color == BLACK)
{
if (child->color == RED)
{
child->color = BLACK;
}
else
delete_case(child);
}

delete p;
}

void delete_case(Node *p)
{
if (p->parent == NULL)
{
p->color = BLACK;
return;
}
if (p->sibling()->color == RED)
{
p->parent->color = RED;
p->sibling()->color = BLACK;
if (p == p->parent->leftTree)
rotate_left(p->sibling());
else
rotate_right(p->sibling());
}
if (p->parent->color == BLACK && p->sibling()->color == BLACK
&& p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK)
{
p->sibling()->color = RED;
delete_case(p->parent);
}
else if (p->parent->color == RED && p->sibling()->color == BLACK
&& p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK)
{
p->sibling()->color = RED;
p->parent->color = BLACK;
}
else
{
if (p->sibling()->color == BLACK)
{
if (p == p->parent->leftTree && p->sibling()->leftTree->color == RED
&& p->sibling()->rightTree->color == BLACK)
{
p->sibling()->color = RED;
p->sibling()->leftTree->color = BLACK;
rotate_right(p->sibling()->leftTree);
}
else if (p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK
&& p->sibling()->rightTree->color == RED)
{
p->sibling()->color = RED;
p->sibling()->rightTree->color = BLACK;
rotate_left(p->sibling()->rightTree);
}
}
p->sibling()->color = p->parent->color;
p->parent->color = BLACK;
if (p == p->parent->leftTree)
{
p->sibling()->rightTree->color = BLACK;
rotate_left(p->sibling());
}
else
{
p->sibling()->leftTree->color = BLACK;
rotate_right(p->sibling());
}
}
}

void insert(Node *p, int data)
{
if (p->value >= data)
{
if (p->leftTree != NIL)
insert(p->leftTree, data);
else
{
Node *tmp = new Node();
tmp->value = data;
tmp->leftTree = tmp->rightTree = NIL;
tmp->parent = p;
p->leftTree = tmp;
insert_case(tmp);
}
}
else
{
if (p->rightTree != NIL)
insert(p->rightTree, data);
else
{
Node *tmp = new Node();
tmp->value = data;
tmp->leftTree = tmp->rightTree = NIL;
tmp->parent = p;
p->rightTree = tmp;
insert_case(tmp);
}
}
}

void insert_case(Node *p)
{
if (p->parent == NULL)
{
root = p;
p->color = BLACK;
return;
}
if (p->parent->color == RED)
{
if (p->uncle()->color == RED)
{
p->parent->color = p->uncle()->color = BLACK;
p->grandparent()->color = RED;
insert_case(p->grandparent());
}
else
{
if (p->parent->rightTree == p && p->grandparent()->leftTree == p->parent)
{
rotate_left(p);
rotate_right(p);
p->color = BLACK;
p->leftTree->color = p->rightTree->color = RED;
}
else if (p->parent->leftTree == p && p->grandparent()->rightTree == p->parent)
{
rotate_right(p);
rotate_left(p);
p->color = BLACK;
p->leftTree->color = p->rightTree->color = RED;
}
else if (p->parent->leftTree == p && p->grandparent()->leftTree == p->parent)
{
p->parent->color = BLACK;
p->grandparent()->color = RED;
rotate_right(p->parent);
}
else if (p->parent->rightTree == p && p->grandparent()->rightTree == p->parent)
{
p->parent->color = BLACK;
p->grandparent()->color = RED;
rotate_left(p->parent);
}
}
}
}

void DeleteTree(Node *p)
{
if (!p || p == NIL)
{
return;
}
DeleteTree(p->leftTree);
DeleteTree(p->rightTree);
delete p;
}
public:

bst()
{
NIL = new Node();
NIL->color = BLACK;
root = NULL;
}

~bst()
{
if (root)
DeleteTree(root);
delete NIL;
}

void inorder()
{
if (root == NULL)
return;
inorder(root);
cout << endl;
}

void insert(int x)
{
if (root == NULL)
{
root = new Node();
root->color = BLACK;
root->leftTree = root->rightTree = NIL;
root->value = x;
}
else
{
insert(root, x);
}
}

bool delete_value(int data)
{
return delete_child(root, data);
}
private:
Node *root, *NIL;
};

int main()
{
cout << "---【紅黑樹】---" << endl;
// 創(chuàng)建紅黑樹
bst tree;

// 插入元素
tree.insert(2);
tree.insert(9);
tree.insert(-10);
tree.insert(0);
tree.insert(33);
tree.insert(-19);

// 順序打印紅黑樹
cout << "插入元素后的紅黑樹:" << endl;
tree.inorder();

// 刪除元素
tree.delete_value(2);

// 順序打印紅黑樹
cout << "刪除元素 2 后的紅黑樹:" << endl;
tree.inorder();

// 析構
tree.~bst();

getchar();
return 0;
}

9 B樹/B+樹

在計算機科學中,B樹(B-tree)是一種樹狀數(shù)據(jù)結構,它能夠存儲數(shù)據(jù)、對其進行排序并允許以O(log n)的時間復雜度運行進行查找、順序讀取、插入和刪除的數(shù)據(jù)結構。B樹,概括來說是一個節(jié)點可以擁有多于2個子節(jié)點的二叉查找樹。與自平衡二叉查找樹不同,B-樹為系統(tǒng)最優(yōu)化大塊數(shù)據(jù)的讀和寫操作。B-tree算法減少定位記錄時所經(jīng)歷的中間過程,從而加快存取速度。普遍運用在數(shù)據(jù)庫和文件系統(tǒng)。

                             ——維基百科

B 樹可以看作是對2-3查找樹的一種擴展,即他允許每個節(jié)點有M-1個子節(jié)點。

  • 定義任意非葉子結點最多只有M個兒子;且M>2;
  • 根結點的兒子數(shù)為[2, M];
  • 除根結點以外的非葉子結點的兒子數(shù)為[M/2, M];
  • 每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
  • 非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
  • 非葉子結點的指針:P[1], P[2], …, P[M];其中P[1]指向關鍵字小于K[1]的 子樹,P[M]指向關鍵字大于K[M-1]的子樹,其它P[i]指向關鍵字屬于(K[i-1], K[i])的子樹;
  • 所有葉子結點位于同一層;

如:(M=3)

算法思路

從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬范圍的兒子結點;重復,直到所對應的兒子指針為空,或已經(jīng)是葉子結點;

  • 關鍵字集合分布在整顆樹中;
  • 任何一個關鍵字出現(xiàn)且只出現(xiàn)在一個結點中;
  • 搜索有可能在非葉子結點結束;
  • 其搜索性能等價于在關鍵字全集內做一次二分查找;
  • 自動層次控制;

代碼

BTNode* BTree_recursive_search(const BTree tree, KeyType key, int* pos)
{
int i = 0;
while (i < tree->keynum && key > tree->key[i])
{
++i;
}

// 查找關鍵字
if (i < tree->keynum && tree->key[i] == key)
{
*pos = i;
return tree;
}

// tree 為葉子節(jié)點,找不到 key,查找失敗返回
if (tree->isLeaf)
{
return NULL;
}

// 節(jié)點內查找失敗,但 tree->key[i - 1]< key < tree->key[i],
// 下一個查找的結點應為 child[i]

// 從磁盤讀取第 i 個孩子的數(shù)據(jù)
disk_read(&tree->child[i]);

// 遞歸地繼續(xù)查找于樹 tree->child[i]
return BTree_recursive_search(tree->child[i], key, pos);
}

B+樹

B+樹是B樹的變體,也是一種多路搜索樹:

其定義基本與B-樹同,除了:

  • 非葉子結點的子樹指針與關鍵字個數(shù)相同;
  • 非葉子結點的子樹指針P[i],指向關鍵字值屬于[K[i], K[i+1])的子樹, B樹是開區(qū)間
  • 為所有葉子結點增加一個鏈指針;
  • 所有關鍵字都在葉子結點出現(xiàn);

如:(M=3)

1

算法思路

B+的搜索與B樹也基本相同,區(qū)別是B+樹只有達到葉子結點才命中(B樹可以在 非葉子結點命中),其性能也等價于在關鍵字全集做一次二分查找;

  • 所有關鍵字都出現(xiàn)在葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字恰好是有序的;
  • 不可能在非葉子結點命中;
  • 非葉子結點相當于是葉子結點的索引(稀疏索引),葉子結點相當于是存儲(關鍵字)數(shù)據(jù)的數(shù)據(jù)層;
  • 更適合文件索引系統(tǒng);

參考資料

  1. https://www.sohu.com/a/296278527_478315
  2. https://www.cnblogs.com/exzlc/p/12203744.html
  3. 部分配圖來源于網(wǎng)絡


本文授權轉載自公眾號“C語言與CPP編程”,作者自成一派123

-END-




推薦閱讀



【01】C語言十大經(jīng)典排序算法(動態(tài)演示+代碼,值得收藏)
【02】C語言、嵌入式中幾個非常實用的宏技巧
【03】C語言最全入門筆記
【04】絕對能檢測你C語言基礎水平的5個面試題
【05】C語言為何不會過時?你需要掌握多少種語言?

免責聲明:整理文章為傳播相關技術,版權歸原作者所有,如有侵權,請聯(lián)系刪除

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關注更多精彩內容

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或將催生出更大的獨角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產業(yè)博覽會開幕式在貴陽舉行,華為董事、質量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉