红黑树
1.节点分为红色或者黑色;
2.根节点必为黑色;
3.叶子节点都为黑色,且为null;
4.连接到】红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点);
5.从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点;
6.新加入到红黑树的节点为红色节点;
引申:兄弟节点的高度不可能比当前节点大2或以上。
Case 1 | 当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。 | (01) 将“父节点”设为黑色。(02) 将“叔叔节点”设为黑色。 (03) 将“祖父节点”设为“红色”。 (04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。 |
Case 2 | 左左情况:当前节点的父节点是左孩子且为红色,叔叔节点是黑色,且当前节点是其父节点的左孩子 | (01) 将“父节点”设为“黑色”。(02) 将“祖父节点”设为“红色”。 (03) 以“祖父节点”为支点进行右旋。 |
Case 3 | 左右情况:当前节点的父节点是左孩子且为红色,叔叔节点是黑色,且当前节点是其父节点的右孩子 | (01) 将“父节点”作为“新的当前节点”。(02) 以“新的当前节点”为支点进行左旋(将左右情况转换成左左情况)。 |
插入51,首选进行变色。变色操作到祖父节点为止。如果曾祖为黑色,插入操作结束,否则以祖父为当前节点继续下一轮操作。
插入65,左左旋转
插入67,左右旋转
- 删除
对任何节点的删除都可以转换成对叶子节点的删除。
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。
情况4应该修正为,兄弟节点不存在红色的子节点。
删除修复的思路可以总结为,把兄弟节点所在额子树中多余的红色节点转换成黑色,并从中借一个黑色节点过来填补空缺。
递归过程可以看成当前节点所在的分支,因为以当前节点为根节点的子树中缺少了黑色节点,从而不满足5条性质。和插入不同的是,先调整再删除。
情况5应该修正为:将兄弟节点修改为黑色,父节点改为红色,然后再以父节点为支点左旋。这样可以转换成兄弟节点为黑色的情况。
快速排序
function partition(arr, left ,right) {
// 分区操作
var pivot = left, // 设定基准值(pivot)
index = pivot + 1;
for (var i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index-1;
index表示最左边大于等于arr[pivot]的位置
返回的index-1是排好序的位置
颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:
输入:nums = [0]
输出:[0]
示例 4:
输入:nums = [1]
输出:[1]
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
一般写法 leetcode75 60% 10%
class Solution {
public:
void sortColors(vector<int>& nums) {
if(nums.size()<=1){
return;
}else{
sort_func(nums,0,nums.size());
return;
}
}
void sort_func(vector<int>& arr, int left, int len){
if(len<=1){
return;
}else{
int pivot=partition(arr,left,len);
sort_func(arr,left,pivot-left);
sort_func(arr,pivot+1,len+left-pivot-1);
}
}
int partition(vector<int>& arr, int left, int len){
// 以arr[left]为pivot_value, left 先空出来,从left+1开始循环,
//每次循环试图找到最靠左的且值大于等于pivot_value的位置
int pivot=-1;
int pivot_value=arr[left];
for(int i=left+1;i<left+len;i++){
if(arr[i]<pivot_value && pivot>0){
int tmp=arr[pivot];
arr[pivot]=arr[i];
arr[i]=tmp;
pivot++;
}else if(arr[i]>=pivot_value && pivot==-1){
pivot=i;
}
}
if (pivot==-1){
pivot=left+len-1;
arr[left]=arr[left+len-1];
arr[left+len-1]=pivot_value;
}else{
pivot=pivot-1;
arr[left]=arr[pivot];
arr[pivot]=pivot_value;
}
return pivot;
}
};
双指针,三区域写法
class Solution {
public:
void sortColors(vector<int>& nums) {
if(nums.size()<=1){
return;
}else{
sort_func(nums,0,nums.size());
return;
}
}
void sort_func(vector<int>& arr, int left, int len){
if(len<=1){
return;
}else{
int* pivots=partition(arr,left,len);
sort_func(arr,left,pivots[0]-left);
sort_func(arr,pivots[1]+1,len+left-pivots[1]-1);
delete[] pivots;
}
}
int* partition(vector<int>& arr, int left, int len){
int* p= new int[2];
p[0]=left;
p[1]=left+len-1;
int cur=left;
int pivot_value=arr[left];
while(cur<=p[1]){
if (arr[cur]<pivot_value){
int tmp=arr[cur];
arr[cur]=arr[p[0]];
arr[p[0]]=tmp;
p[0]++;
cur++;
}else if(arr[cur]>pivot_value){
int tmp=arr[cur];
arr[cur]=arr[p[1]];
arr[p[1]]=tmp;
p[1]--;
//注意这里不需要cur++,因为p1指向的是未知区域,和cur交换之后还需要继续判断
}else{
cur++;
}
}
return p;
}
};
每次p[0]到p[1]是已经排好序的部分
一次遍历版本
直接把0换到最左边,2换到最右边
public:
void sortColors(vector<int> &nums) {
int size = nums.size();
if (size < 2) {
return;
}
// all in [0, zero) = 0
// all in [zero, i) = 1
// all in [two, len - 1] = 2
int zero = 0;
int two = size;
int i = 0;
while (i < two) {
if (nums[i] == 0) {
swap(nums[zero], nums[i]);
zero++;
i++;
} else if (nums[i] == 1) {
i++;
} else {
two--;
swap(nums[i], nums[two]);
}
topk
剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
int len=arr.size();
if(k==0){
return vector(arr.begin(),arr.begin()+k);
}
sort(arr,0,len-1,k);
return vector(arr.begin(),arr.begin()+k);
}
void sort(vector<int>& arr, int left, int right, int k){
if(left==right){
return;
}
int pivot=partition(arr,left,right);
if(pivot==k-1){
return;
}else if(pivot>k-1){
sort(arr,left,pivot-1,k);
}else{
sort(arr,pivot+1,right,k);
}
return;
}
int partition(vector<int>& arr, int left, int right){
if(left==right){
return left;
}
int tmp;
int index=left+1;
int value=arr[left];
for(int i=left+1;i<=right;i++){
if(arr[i]<value){
tmp=arr[i];
arr[i]=arr[index];
arr[index]=tmp;
index++;
}
}
tmp=arr[left];
arr[left]=arr[index-1];
arr[index-1]=tmp;
return index-1;
}
};
归并排序
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
class Solution {
public:
int result;
int reversePairs(vector<int>& nums) {
int len=nums.size();
if(len==0){
return 0;
}
result=0;
order(nums,0,len-1);
return result;
}
void order(vector<int>& nums,int left,int right){
vector<int> help;
if(left==right){
return;
}
int mid=(left+right)/2;
order(nums,left,mid);
order(nums,mid+1,right);
int l=left;
int r=mid+1;
while(l<=mid && r<=right){
if(nums[r]<nums[l]){
result+=mid-l+1;
help.push_back(nums[r]);
r++;
}else{
help.push_back(nums[l]);
l++;
}
}
if(l<=mid){
for(;l<=mid;l++){
help.push_back(nums[l]);
}
}
if(r<=right){
for(;r<=right;r++){
help.push_back(nums[r]);
}
}
for(int i=right;i>=left;i--){
nums[i]=help.back();
help.pop_back();
}
}
};