刷题笔记:高级数据结构专题(第一遍刷搞懂每道题的解法;第二遍刷搞懂这一类题通用的思想和策略;第三遍刷思考什么样的题属于这个类别

Trie树(字典树)概述

#define TRIE_MAX_CHAR_NUM 26

struct TrieNode{
    TrieNode *child[TRIE_MAX_CHAR_NUM];
    bool is_end;
    TrieNode():is_end(false){
        for(int i=0;i<TRIE_MAX_CHAR_NUM;i++){
            child[i]=0;
        }
    }

}
class Trie {
private:
    bool isEnd;
    Trie* next[26];
public:
    Trie() {
        isEnd = false;
        memset(next, 0, sizeof(next));
    }
    
    void insert(string word) {
        Trie* node = this;
        for (char c : word) {
            if (node->next[c-'a'] == NULL) {
                node->next[c-'a'] = new Trie();
            }
            node = node->next[c-'a'];
        }
        node->isEnd = true;
    }
    
    bool search(string word) {
        Trie* node = this;
        for (char c : word) {
            node = node->next[c - 'a'];
            if (node == NULL) {
                return false;
            }
        }
        return node->isEnd;
    }
    
    bool startsWith(string prefix) {
        Trie* node = this;
        for (char c : prefix) {
            node = node->next[c-'a'];
            if (node == NULL) {
                return false;
            }
        }
        return true;
    }
};

 

 

Trie树前序遍历

Trie树获取全部单词

深度优先搜Trie树,然后用栈存单词

Trie树单词插入:

 

LeetCode 211 添加与搜索单词数据结构设计

并查集概述:

 

并查集可以用数组实现:

时间复杂度O(n)

并查集得森林实现:

利用路径压缩将森林压缩得扁平(p得下一个结点=p得下一个结点得下一个结点 即可实现路径压缩)

#include"pch.h"
#include<iostream>
#include<vector>

using namespace std;

class DisjoinSet {
public:
	DisjoinSet(int n) {
		for (int i = 0; i < n; i++) {
			_id.push_back(i);
			_size.push_back(1);
		}
		_count = n;
	}

	int find(int p) {
		while (p != _id[p]) {
			_id[p] = _id[_id[p]];
			p = _id[p];
		}
		return p;
	}

	void union_(int p, int q) {
		int i = find(p);
		int j = find(q);
		if (i == j) {
			return;
		}
		if (_size[i] < _size[j]) {
			_id[i] = j;
			_size[j] += _size[i];
		}
		else {
			_id[j] = i;
			_size[i] += _size[j];
		}
		_count--;
	}

private:
	vector<int> _id;
	vector<int> _size;
	int _count;
};

例1:朋友圈(LeetCode 547-中等

题目:

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例:

输入: 
[[1,1,0],
 [1,1,0],
 [0,0,1]]
输出: 2 
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。

思想:

1.DFD

2.并查集

代码:

利用深搜思想

class Solution {
public:
    int findCircleNum(vector<vector<int>>& M) {
        vector<int> visit(M.size(),0);
        int result=0;
        for(int i=0;i<M.size();i++){
            if(visit[i]==0){
                DFS(M,i,visit);
                result++;
            }
        }
        return result;
    }
private:
    void DFS(vector<vector<int>>& graph,int u, vector<int>& visit){
        visit[u]=1;
        for(int i=0;i<graph[u].size();i++){
            if(graph[u][i]==1 && visit[i]==0){
                DFS(graph,i,visit);
            }
        }
    }
};

线段树概述:

线段树的构造:

//线段树
void build_segment_tree(vector<int> &value, vector<int> &nums, int pos, int left, int right) {
	if (left == right) {
		value[pos] = nums[left];
		return;
	}
	int mid = (left + right) / 2;
	build_segment_tree(value, nums, pos * 2 + 1, left, mid);
	build_segment_tree(value, nums, pos * 2 + 2, mid + 1, right);
	value[pos] = value[pos * 2 + 1] + value[pos * 2 + 2];
}

线段树求和:log(n)

//线段树求和
int sum_range_segment_tree(vector<int> &value,int pos,int left,int right,int gleft,int gright) {
	//没交集直接返回O
	if (right<gleft || left>gright){
		return 0;
	}
	if (left>=gleft && right<=gright){
		return value[pos];
	}
	int mid = (left + right) / 2;
	return sum_range_segment_tree(value, pos * 2 + 1, left, mid, gleft, gright) +
		sum_range_segment_tree(value, pos * 2 + 2, mid + 1, right, gleft, gright);
}

线段树更新: log(n)

//线段树更新
void update_segment_tree(vector<int> &value,int pos,int left,int right,int index,int new_value){
	if (left == index && right == index) {
		value[pos] = new_value;
		return;
	}
	int mid = (left + right) / 2;
	if (index <= mid) {
		update_segment_tree(value, pos * 2 + 1, left, mid, index, new_value);
	}
	else {
		update_segment_tree(value, pos * 2 + 2, mid + 1, right, index, new_value);
	}
	value[pos] = value[pos * 2 + 1] + value[pos * 2 + 2];
}

例:区域和的查询(LeetCode307-中等

题目:

给定一个整数数组  nums,求出数组从索引 i 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。

示例:

Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
  1. 数组仅可以在 update 函数下进行修改。
  2. 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。

思想:

用线段树即可

代码:

class NumArray {
public:
    NumArray(vector<int>& nums) {
        if(nums.size()==0){
            return;
        }
        //一般线段树数组大小是原数组大小长度的4倍
        int n=nums.size()*4;
        value.resize(n,0);
        //构造
        build_segment_tree(value,nums,0,0,nums.size()-1);
        right_end=nums.size()-1;
    }
    
    void update(int i, int val) {
        update_segment_tree(value,0,0,right_end,i,val);
    }
    
    int sumRange(int i, int j) {
        return sum_segment_tree(value,0,0,right_end,i,j);
    }
private:
    //数据
    vector<int> value;
    int right_end;
private:
    //初始化线段树
    void build_segment_tree(vector<int>& value,vector<int>& nums,int pos, int left,int right){
        if(left==right){
            value[pos]=nums[left];
            return;
        }
        int mid=(left+right)/2;
        build_segment_tree(value,nums,pos*2+1,left,mid);
        build_segment_tree(value,nums,pos*2+2,mid+1,right);
        value[pos]=value[pos*2+1]+value[pos*2+2];
    }
    //更新线段树
    void update_segment_tree(vector<int>& value,int pos,int left,int right, int index,int new_value){
        if(left==index && right==index){
            value[pos]=new_value;
            return;
        }
        int mid=(left+right)/2;
        if(index<=mid){
            update_segment_tree(value,pos*2+1,left,mid,index,new_value);
        }
        else{
            update_segment_tree(value,pos*2+2,mid+1,right,index,new_value);
        }
        //每个父节点也要更新
        value[pos]=value[pos*2+1]+value[pos*2+2];
        return;
    }
    //线段树求和
    int sum_segment_tree(vector<int>& value,int pos ,int left,int right,int gleft,int gright){
        if(right<gleft || left>gright){
            return 0;
        }
        if(right<=gright && left >=gleft){
            return value[pos];
        }
        int mid=(left+right)/2;
        return sum_segment_tree(value,pos*2+1,left,mid,gleft,gright)+
        sum_segment_tree(value,pos*2+2,mid+1,right,gleft,gright);
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * obj->update(i,val);
 * int param_2 = obj->sumRange(i,j);
 */

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值