# 查找算法总结(顺序查找、二分查找、二叉树、平衡二叉树、红黑树、散列表hash)

## 常用的数据结构

### 二、有序数组

//递归实现rank
int rank(int key, int lo, int hi){
if(hi < lo) return lo;
int mid = (lo + hi)/2;
if(key < a[mid])
return rank(key, lo, mid);
else if (key > a[mid])
return rank(key, mid, hi);
else
return mid;
}
//迭代实现rank
int rank(int key){
int lo = 0, hi = N-1;
while(lo <= hi){
int mid = (lo + hi)/2;
if (key < a[mid])
hi = mid -1;
else if (key > a[mid])
lo = mid +1;
else
return mid;
}
return lo;
}

int get(int key){
int i = rank(key);
if(i < N && keys[i] == key)
return vals[i];
else
return null;
}

void put(int key, int value){
int i = rank(key);
if(i < N && keys[i]==key){
vals[i] = value;
return;
}
for(int j=N; j>i; j--){
keys[j] = keys[j-1];
vals[j] = vals[j-1];
}
keys[i] = key;
vals[i] = val;
N++;
}

### 三、二叉树

int get(Node x, int key){
if(x == null)
return null;
if(key < x.key)
return get(x.left, key);
else if (key > x.key)
return get(x.right, key);
else
return x.value;
}

void put(int key, int val){
root = put(root, key, val);
}

Node put(Node x, int key, int val){
if (x == null)
return new Node(key, val, 1);//最后一个参数为结点计数器
if(key < x.key)
x.left = put(x.left, key, val);
else if (key > x.key)
x.right = put(x.right, key, val);
else
x.value = val;
x.N = size(x.left) + size(x.right) + 1;
return x;
}

Node deleteMin(Node x){
if(x.left == null)
return x.right;
x.left = deleteMin(x.left);
x.N = size(x.left) + size(x.right) + 1;
return x;
}

Node delete(Node x, int key){
if(x == null)
return null;
if(key < x.key)
x.left = delete(x.left, key);
else if (key > x.key)
x.right = delete(x.right, key);
else{
if(x.right == null)
return x.left;
if (x.left == null)
return x.right;
//找到右子树上的最小结点，将其填补到当前位置，其左子树就是原有左子树，右子树就是右子树删除最小点后的子树
// 这里的填补是一个递归操作，将填补的结点x返回了，作为原有结点的父节点的子节点
Node t = x;
x = min(t.right);
x.right = deleteMin(t.right);
x.left = t.left;
}
x.N = size(x.left) + size (x.right) + 1;
return x;
}

### 四、平衡二叉树

1、2-3查找树

3-结点含有两个键和三个结点，设两个键为S和T(S<T)，则左结点均小于S，中结点介于S和T之间，右结点大于T。完美的2-3查找树中所有的空链接应该在同一层中。

2-3查找树的查找和二叉树非常类似，判断键所在区间，不断向下查找即可。这里需要关注的是2-3查找树的插入操作，如何插入能够一直保证树的平衡：

1) 向2-结点插入：将2-结点变换为3-结点即可；

2) 向一颗只含有一个3-结点的树插入：将3-结点变为4-结点，然后将其分解为2-3树，树高增加一层；

3) 向一个父结点为2-结点的3-结点插入：将3-结点变为4-结点，再将其中键提取至父结点，将父结点变为一个3-结点；

4) 向一个父结点为3-结点的3-结点插入：按照3) 中的方法不断向上替换直至遇到一个2-结点，如果根结点也是3-结点则使用2) 中的方法将树高增加一层。

2、红黑二叉查找树

2-结点进行插入：

void put(int key, int val){
root = put(root, key, val);
root.color = BLACK;
}
Node put(Node h, int key, int val){
if (h == null)
return new Node(key, val, 1, RED);
if (key < h.left)
h.left = put(h.left, key, val);
else if (key > h.right)
h.right = put(h.right, key, val);
else
h.val = val;

if(isRed(h.right) && !isRed(h.left))
h = rotateLeft(h);
if (isRed(h.left) && isRed(h.left.left))
h = rotateRight(h);
if(isRed(h.right) && isRed(h.left))
flipColors(h);
h.N = size(h.left) + size(h.right) + 1;
return h;
}
//右链接为红时，向左旋转
Node rotateLeft(Node h){
Node x = h.right;
h.right = x.left;
x.left = h;
x.color = h.color;
h.color = RED;
x.N = h.N;
h.N = 1 + size(h.left) + size(h.right);
return x;
}
//左链接有连续两个红链接时，向右旋转
Node rotateRight(Node h){
Node x = h.left;
h.left = x.right;
x.right = h;
x.color = h.color;
h.color = RED;
x.N = h.N;
h.N = 1 + size(h.left) + size(h.right);
return x;
}
//两个子链接都是红色时，变换颜色
void flipColors(Node h){
h.color = RED;
h.left.color = BLACK;
h.right.color = BLACK;
}

### 五、散列表hash

1) 拉链法

2) 线性探测法

void put(int key, int val){
if(N>=M/2)
resize(2*M);//将数组扩大
int i;
for(i=hash(key); keys[i]!=null; i=(i+1)%M)
if(keys[i].equals(key)){
vals[i] = val;
return;
}
keys[i] = key;
vals[i] = val;
N++;
}
int get(int key){
for(int i=hash(key); keys[i] != null; i=(i+1)%M)
if(keys[i].equals(key))
return vals[i];
return null;
}

void delete(int key){
if(!contains(key))  return;
int i = hash(key);
while(!(key == keys[i]))
i = (i+1)%M;
keys[i] = null;
vals[i] = null;
i = (i+1)%M;
while(keys[i] != null){
int keyToRedo = keys[i];
int valToRedo = vals[i];
keys[i] = null;
vals[i] = null;
N--;
put(keyToRedo, valToRedo);
i = (i+1)%M;
}
N--;
if(N>0 && N == M/8)
resize(M/2);
}

04-22
12-03

12-24
03-25
04-02
10-12 1万+
09-12 1万+
05-21 39
12-24 1万+
11-11 5984