牛の顧客ネットワークアルゴリズムの概要(2)

:ツリー

1.折り紙の問題

[タイトル]テーブルの上に縦にいくつかの点に注意して、下から上に一度一枚の紙を折ら、拡張後にしわを押してください。この場合、倍、すなわち、投影折り目の方向が紙の表面をバック向け、凹面です。連続二回半分に折った場合は、上向きの拡大を低く倍ストリップから押し出され、その後、3つのひだがあり、上から下へは倍、倍としわです。下のN回折り畳ま連続ストリップの代表的なものであるから、入力パラメータNを考えると、上方に折り、上から下にすべての方向を印刷してください。例えば:N = 1、印刷:ダウンN = 2、印刷:ダウン上下

/**
 * 折纸问题,就是一棵左结点为down,右结点为up的二叉树
 */
public class Paper {
    static class TreeNode{
        private String val;
        private TreeNode left;
        private TreeNode right;

        public TreeNode(String val) {
            this.val = val;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    ", left=" + left +
                    ", right=" + right +
                    '}';
        }
    }
    public static void print(int N){
        if (N < 1)
            return;
        TreeNode root = new TreeNode("down");
        build(root,N);
        print(root);
    }

    private static void print(TreeNode node){
        if (node.left != null)
            print(node.left);
        System.out.println(node.val);
        if (node.right != null)
            print(node.right);
    }

    private static void  build(TreeNode node,int N){
        if (N <= 1)
            return;
        node.left = new TreeNode("down");
        node.right = new TreeNode("up");
        build(node.left,N-1);
        build(node.right,N-1);
    }


    public static void main(String[] args) {
        print(3);
    }
}

非再帰的トラバース後に前記第1バイナリシーケンス、配列、

    //递归前序遍历
    public static void pre1(TreeNode node){
        if (node == null)
            return;
        System.out.printf(node.val+" ");
        pre1(node.left);
        pre1(node.right);
    }

    //非递归前序遍历
    public static void pre2(TreeNode node){
        if (node == null)
            return;
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || node != null){
            //把所有左结点加入到栈
            if (node != null){
                System.out.printf(node.val+" ");
                stack.push(node);
                node = node.left;
            }
            //左结点都为空,把上一个结点的右结点入栈
            else
                node = stack.pop().right;
        }
    }

    //递归中序遍历
    public static void in1(TreeNode node){
        if (node == null)
            return;
        in1(node.left);
        System.out.printf(node.val+" ");
        in1(node.right);
    }

    //非递归中序遍历
    public static void in2(TreeNode node){
        if (node == null)
            return;
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || node != null){
            //把所有左结点加入到栈
            if (node != null){
                stack.push(node);
                node = node.left;
            }
            //左结点都为空,把上一个结点的右结点入栈
            else
            {
                node = stack.pop();
                System.out.printf(node.val+" ");
                node = node.right;
            }
        }
    }

    //递归后序遍历
    public static void post1(TreeNode node){
        if (node == null)
            return;
        post1(node.left);
        post1(node.right);
        System.out.printf(node.val+" ");
    }

    //非递归后序遍历
    public static void post2(TreeNode node){
        if (node == null)
            return;
        ArrayList<TreeNode> nodes = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || node != null){
            //把所有右结点加入到栈
            if (node != null){
                nodes.add(node);
                stack.push(node);
                node = node.right;
            }
            //右结点都为空,把上一个结点的左结点入栈
            else
                node = stack.pop().left;
        }
        Collections.reverse(nodes);
        for (TreeNode node1:
             nodes) {
            System.out.printf(node1.val+" ");
        }
    }

3.モリストラバーサル

特徴:

  • 時間計算量はO(N)であり、空間的な複雑さはO(1)であり、ツリートラバーサルプロセスは、形状を変化させません
  • ノードは左のサブツリーを持っているため、木の無い左の子ノードのために、二回だけ1回の訪問を訪問しました
  • 一般に、左、現在のノードは、現在のノードに沿って右方向

要件:

  • 、何の左部分木cur変換しない右に移動(左無い道路、その後、私は右に直接移動します)
  • CURは右端の左部分木のノードmostRightを見つけ、サブツリーを残しています
    • mostRight右の子は空です:mostRight = CUR、CUR移動左に(見に左に行く、一度自分自身のラベル)
    • (右に直接、自分のマークを見て、私は左の側を通って行っている)mostRight = nullを、CUR移動右へ:mostRight右の子はCURです
/**
 * morris遍历
 */
public class MorrisTravel {
    static class TreeNode {
        private int val;
        private TreeNode left;
        private TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    ", left=" + left +
                    ", right=" + right +
                    '}';
        }
    }

    public static void inTravel(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur = root;
        TreeNode next = null;
        while (cur != null) {
            next = cur.left;
           //有左子树
            if (next != null) {
                while (next.right != cur && next.right != null)
                    next = next.right;
                //第一次访问,改一下指针,向左走(中断)
                if (next.right == null) {
                    next.right = cur;
                    cur = cur.left;
                    continue;
                }
                //第二次访问,修复指针,向右走↓
                else
                    next.right = null;
            }

            //左子树遍历完成(左-根-右)
            System.out.printf(cur.val + " ");
            cur = cur.right;
        }
    }

    public static void preTravel(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur = root;
        TreeNode next = null;
        while (cur != null) {
            next = cur.left;
           //有左子树
            if (next != null) {
                while (next.right != cur && next.right != null)
                    next = next.right;
                //第一次访问,改一下指针,向左走(中断)
                if (next.right == null) {
                     next.right = cur;
                    //(根-左)
                    System.out.printf(cur.val + " ");
                    cur = cur.left;
                    continue;
                }
                //第二次访问,修复指针,向右走↓
                else
                    next.right = null;
            }
            //左子树遍历完成
            else
                //(根-右)
                System.out.printf(cur.val + " ");
            cur = cur.right;

        }
    }

    public static void postTravel(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur = root;
        TreeNode next = null;
        while (cur != null) {
            next = cur.left;
           //有左子树
            if (next != null) {
                while (next.right != cur && next.right != null)
                    next = next.right;
                //第一次访问,改一下指针,向左走(中断)
                if (next.right == null) {
                    next.right = cur;
                    cur = cur.left;
                    continue;
                }
                //第二次访问,修复指针,向右走↓
                else
                {
                    next.right = null;
                    //已经走完左边和右边了(左-右-根)
                    //打印左子树的倒置右结点
                    reverse(cur.left);
                }
            }
            cur = cur.right;

        }
        //打印根节点的倒置右结点
        //左边和右边都走完了
        reverse(root);
    }

    private static void reverse(TreeNode node){
        if (node == null)
            return;
        TreeNode next = node.right;
        TreeNode temp = null;
        node.right = null;
        while (next != null){
            temp = next.right;
            next.right = node;
            node = next;
            next = temp;
        }
        next = node;
        while (next != null){
            System.out.printf(next.val + " ");
            next = next.right;
        }
        next = node.right;
        node.right = null;
        while (next != null){
            temp = next.right;
            next.right = node;
            node = next;
            next = temp;
        }
    }
}

4.バイナリツリー内のノードの後継者を探します

[タイトル]次に、以下のように新しいバイナリツリーノードの種類があるがあります:

public class Node { 
	public int value; 
	public Node left; 
	public Node right; 
	public Node parent;
	public Node(int data) { this.value = data; }
}

従来のバイナリツリーノード構造よりも親ノードを指す親ポインタよりもこの構成。バイナリノードタイプのノードを仮定する、ツリー親ポインタ内の各ノードは、適切にその親ノード、親ヌルへの最初のノードに向けられています。バイナリーツリー内のノードへの唯一のノード、ノードの後続ノードを返す関数を実装します。二分木の配列先行順走査で、ノードの次ノードノードが後続ノードと呼ばれます。

    public static Node nextNode(Node node){
        if (node == null)
            return null;
        //中序遍历是左-中-右
        //如果有右结点,那么就在下一层找,找到右孩子的最左边的结点
        if (node.right != null){
            node = node.right;
            while (node != null && node.left != null) {
                node = node.left;
            }
            return node;
        }
        //如果没有右结点,那么就只能往上一层查找,找到一个作为左结点的父结点
        else
            while (node.parent != null && node != node.parent.left)
                node = node.parent;
            return node.parent;
    }

5.プレフィックスツリーは何ですか?プレフィックスツリーを生成する方法は?

/**
 * 前缀树
 */
public class PrefixTree {
    static class PrefixNode {
        //经过结点的单词个数
        private int pass;
        //单词长度
        private int end;
        //下一个结点
        private HashMap<Integer, PrefixNode> nexts;

        public PrefixNode() {
            pass = 0;
            end = 0;
            nexts = new HashMap<>();
        }

        @Override
        public String toString() {
            return "PrefixNode{" +
                    "pass=" + pass +
                    ", end=" + end +
                    ", nexts=" + nexts +
                    '}';
        }
    }
    private PrefixNode root;

    public PrefixTree() {
        root = new PrefixNode();
    }

    public void insert(String word){
        if (word == null || word.length() == 0)
            return;
        PrefixNode node = root;
        PrefixNode next;
        for (int i = 0; i < word.length(); i++) {
            char s = word.charAt(i);
            next = node.nexts.get((int)s);
            if (next == null)
                 next = new PrefixNode();
            node.nexts.put((int)s,next);
            next.pass++;
            node = next;
        }
        node.end++;
    }

    public void remove(String word){
        if (prefixSize(word) != 0)
        {
            PrefixNode node = root;
            PrefixNode next;
            for (int i = 0; i < word.length(); i++) {
                char s = word.charAt(i);
                next = node.nexts.get((int)s);
                if (--next.pass == 0){
                    node.nexts.put((int)s,null);
                    return;
                }
                node = next;
            }
            node.end--;
        }
    }

    public int prefixSize(String prefix){
        PrefixNode node = root;
        if (prefix == null || prefix.length() == 0)
            return 0;
        int index = 0;
        while (index < prefix.length() && node != null)
        {
            char c = prefix.charAt(index++);
            node = node.nexts.get((int)c);
        }
        if (node == null)
            return 0;
        return node.pass;
    }

    public int count(String word){
        PrefixNode node = root;
        if (word == null || word.length() == 0)
            return 0;
        int index = 0;
        while (index < word.length() && node != null)
        {
            char c = word.charAt(index++);
            node = node.nexts.get((int)c);
        }
        if (node == null)
            return 0;
        return node.end;
    }

    @Override
    public String toString() {
        return "PrefixTree{" +
                "root=" + root +
                '}';
    }
}

プリントアレー

文字列型配列ARR1、ARR2型の別の文字列配列。文字がARR1されたARR2はで表示されますか?印刷してください

ARR1が発生すると、文字が文字列のプレフィックスにされたARR2?印刷してください

    public static void main(String[] args) {
        PrefixTree tree = new PrefixTree();
        String[] arr1 = {"abc","abd","bcd"};
        String[] arr2 = {"abd","cc","ab"};

        //打印数组
        for (int i = 0; i < arr1.length; i++) {
            tree.insert(arr1[i]);
        }
        System.out.println("出现的字符:");
        for (int i = 0; i < arr2.length; i++) {
            if (tree.count(arr2[i]) > 0)
                System.out.println(arr2[i]);
        }
        System.out.println("作为前缀的字符:");
        for (int i = 0; i < arr2.length; i++) {
            if (tree.prefixSize(arr2[i]) > 0)
                System.out.println(arr2[i]);
        }

    }

II:ブルームフィルタ

役割:ブラックリストは、特定の情報をフィルタリングするために使用大きなファイルです。情報がブラックリストに属している場合、それがフィルタリングされます。情報がブラックリストに属していない場合もミスをろ過してもよいです。

構造:配列要素がある大きなアレイ、1ビット

位置情報:

  1. 配列インデックスを配置:X /桁
  2. インデックスの数字の位置:X%桁

ブラック説明:

  1. K準備ハッシュ関数
  2. 情報Kのハッシュ値を取得するそれぞれのハッシュ関数、対応するKビットは黒説明します

検出:

  1. Kのハッシュ値を取得するための情報、それぞれのハッシュ関数
  2. 対応するビットが黒である場合、傍受は、黒色のビットが存在しない場合、次いでせ

特徴:

  • かかわらず、単一のサンプルの大きさの
  • サンプルの数および関連する誤り率

3:一貫性のあるハッシュ

元の構造

  1. クライアントは、キー、要求値を送信します
  2. サーバは、鍵、サーバモジュールの数、サーバXのかを判断し、サービス提供にハッシュ関数によりハッシュ値を取得します
  3. Xサーバが提供するサービス

問題:

  • サーバーを増やし、削除、高いデータ移行コスト
  • コードの変更

改良された構造

  1. 環を形成するように接続されたサーバごとに得られたハッシュ値によってIPサーバ、
  2. クライアントは、キー、要求値を送信します
  3. サーバハッシュ関数は鍵のハッシュ値を取得することにより、リングは、リングのXサーバは、次の方向を時計回りに決定ヒット(二分法により決定されます)
  4. Xサーバが提供するサービス

特徴:

  • サーバー・X1を追加した後、時計回りの方向X2の次のサーバを検索し、データX1-X0のX1を渡すために彼に尋ねました
  • プライマリサーバーの数が少ない、偏荷重
  • サーバーを追加した後、負荷の不均衡

その後、改善された構造

  1. 各サーバは、ルーティングテーブルに仮想ノードの一定量を割り当て
  2. 各仮想サーバーの各ノードのハッシュ値を求めることにより、サーバは、環を形成するように接続されました

4:ハッシュテーブル

1.互いに素セット

  1. 検索の2つの要素は、同じセットに属しています
  2. 一つの大きなコレクションに二組
/**
 * 并查集
 */
public class UnionSet {
    static class Node{
        private int val;

        public Node(int val) {
            this.val = val;
        }
    }
    //记录每个节点的代表节点
    HashMap<Node,Node> fathers;
    //记录每个代表节点的集合个数
    HashMap<Node,Integer> sizes;

    public UnionSet(List<Node> nodes){
        fathers = new HashMap<>();
        sizes = new HashMap<>();
        build(nodes);
    }

    //初始化
    private void build(List<Node> nodes){
        fathers.clear();
        sizes.clear();
        for (Node node :
                nodes) {
            fathers.put(node, node);
            sizes.put(node,1);
        }
    }

    //查找代表节点
    private Node findPresent(Node node){
        Node present = fathers.get(node);
        //找出代表节点为本身的节点
        if (present != node) {
            present = findPresent(present);
        }
        //找到之后,把其他节点都更新代表节点,加快查询速度
        fathers.put(node,present);
        return present;
    }

    //判断是否是同一个集合
    public boolean isSame(Node a,Node b){
        if (a == null || b == null)
            return false;
        return findPresent(a) == findPresent(b);
    }

    //合并集合
    public void union(Node a,Node b){
        if (a == null || b == null || isSame(a,b))
            return;
        int size1 = sizes.get(a);
        int size2 = sizes.get(b);
        if (size1 > size2) {
            fathers.put(b, a);
            sizes.put(a,size1+size2);
        }
        else {
            fathers.put(a, b);
            sizes.put(b,size1+size2);
        }
    }
}

2.諸島問題

各位置は、自分自身であることができる2つだけマトリックス0及び1の値の種類とは、下、左、および右の位置に接続されています。

一緒に1がある場合、この部分はどのように多くの島々の行列を求めて、島と呼ばれていますか?

たとえば:
0 0 0 1 1 0
。1 1 1 0 0 1
。1 1 0 0 0 0
0 0 0 0 0 0
行列は、3つの島を有します

/**
 * 岛问题
 */
public class Island {
    static int sum = 0;

	//遍历岛
    public static int getIsland(int[][] matrix){
        if (matrix == null || matrix[0] == null)
            return 0;
        int N = matrix.length;
        int M  = matrix[0].length;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (matrix[i][j] == 1){
                	//找到有病毒的岛,开始感染
                    sum++;
                    getIsland(matrix,i,j,N,M);
                }
            }
        }
        return sum;
    }
    //不断感染,直到没有病菌
    public static void getIsland(int[][] matrix,int x,int y,int N,int M){
        if (x < 0 || y < 0 || x >= N || y >= M || matrix[x][y] != 1)
            return;
        matrix[x][y] = 2;
        getIsland(matrix,x+1,y,N,M);
        getIsland(matrix,x-1,y,N,M);
        getIsland(matrix,x,y+1,N,M);
        getIsland(matrix,x,y-1,N,M);

    }
    public static void main(String[] args) {
        int[][] matrix = {{0,0,1,0,1,0},{1,1,1,0,1,0},{1,0,0,1,0,0},{0,0,0,0,0,0}};
        System.out.println(getIsland(matrix));
    }
}

2.常にデータ・ストリームの中央値を見つけます

[タイトル]を押して、唾番号を保持するのに十分なスペースを持っていると仮定すると、データ・ストリームの整数の安定した流れを吐き出す必要があります。名前の構造MedianHolderを設計してください、前MedianHolderの全て吐き出す中央値は、いつでも取得することができます。

[条件]

  1. 数N MedianHolder放電が保存されている場合には、いつでも新しい番号は、プロセスMedianHolder、O(logN個)の時間計算量に加算されます。
  2. 中央の吐出プロセスの全体数Nをした、時間の複雑さはO(1)であります
/**
 * 保存中位数
 */
public class MedianHolder {
    class MyComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            if (o1 > o2)
                return -1;
            else if(o1 < o2)
                return 1;
            else
                return 0;
        }
    }
    //最小堆和最大堆
    PriorityQueue<Integer> min = new PriorityQueue<>();
    PriorityQueue<Integer> max = new PriorityQueue<>(new MyComparator());

    //最小堆只添加比自己大的数字,这样才不用调整
    //最大堆只添加比自己小的数字,或者比自己大又比其他小的数字
    //维持两个堆的平衡
    public void add(int num){
        if (max.isEmpty() || num <= max.peek())
            max.add(num);
        else if (min.isEmpty() || num >= min.peek())
            min.add(num);
        else
            max.add(num);
        modify();
    }

    //保证两个堆最多相差一个
    private void modify(){
        if (min.size() - max.size() > 1)
            max.add(min.poll());
        if (max.size() - min.size() > 1)
            min.add(max.poll());
    }

    //奇数个数字,则在多一个数量的堆里面
    //偶数个数字,取中间值
    public int get(){
        if (max.isEmpty() && min.isEmpty())
            return Integer.MIN_VALUE;
        if (min.isEmpty())
            return max.peek();
        if (max.isEmpty())
            return min.peek();
        int minSize = min.size();
        int maxSize = max.size();
        if ((maxSize+minSize) % 2 == 0)
            return (max.peek() + min.peek())/2;
        else if (maxSize > minSize)
            return max.peek();
        else
            return min.peek();
    }
}

おすすめ

転載: blog.csdn.net/weixin_36904568/article/details/94056371