1. 程式人生 > >牛客網演算法基礎培訓第三期 第三課

牛客網演算法基礎培訓第三期 第三課

基礎資料結構(一)

1)陣列,連結串列,陣列,佇列,棧
2)雜湊函式、雜湊表,一致性雜湊和布隆過濾器
3)堆(優先順序佇列,赫夫曼編碼相關問題)

知識點總結

佇列 棧連結串列 列印矩形
轉區域性為巨集觀思想

題目一 用陣列結構實現大小固定的佇列和棧

(實現佇列的時候是一個解耦的比其他課程更好的方法,一定要看一下思路)

public class Code_01_Array_To_Stack_Queue {
    //用一個index下標記位置只對index下標的值操作,天然實現
    public static class ArrayStack {
        private
Integer[] arr; private Integer size; public ArrayStack(int initSize) { if (initSize < 0) { throw new IllegalArgumentException("The init size is less than 0"); } arr = new Integer[initSize]; size = 0; } public
Integer peek() { if (size == 0) { return null; } return arr[size - 1]; } public void push(int obj) { if (size == arr.length) { throw new ArrayIndexOutOfBoundsException("The queue is full"); } arr[size++] = obj; } public
Integer pop() { if (size == 0) { throw new ArrayIndexOutOfBoundsException("The queue is empty"); } return arr[--size]; } } /* * 這裡用size對end和start做了解耦 * end-->新進的數放的位置 * start-->出隊的數從哪拿 * 優於那種start追end的結構,多一個空餘位置判斷是否滿,更容易實現也容易理解 * size!=0的時候能拿數(pop) * size!=length的時候能入隊(push) * 在已經滿的佇列push的時候,end位置從陣列末尾跳到陣列開頭 */ public static class ArrayQueue { private Integer[] arr; private Integer size; private Integer first; private Integer last; public ArrayQueue(int initSize) { if (initSize < 0) { throw new IllegalArgumentException("The init size is less than 0"); } arr = new Integer[initSize]; size = 0; first = 0; last = 0; } public Integer peek() { if (size == 0) { return null; } return arr[first]; } //入隊 size++ public void push(int obj) { if (size == arr.length) { throw new ArrayIndexOutOfBoundsException("The queue is full"); } size++; arr[last] = obj; last = last == arr.length - 1 ? 0 : last + 1; } //出隊size-- public Integer poll() { if (size == 0) { throw new ArrayIndexOutOfBoundsException("The queue is empty"); } size--; int tmp = first; first = first == arr.length - 1 ? 0 : first + 1; return arr[tmp]; } } public static void main(String[] args) { } }

題目二 實現一個特殊的棧,在實現棧的基本功能的基礎上,再實現返回棧中最小元素的操作。

【要求】
1.pop、push、getMin操作的時間複雜度都是O(1)。
2.設計的棧型別可以使用現成的棧結構。


import java.util.Stack;
/*
 * 修改基本棧
 * 加一個最小數棧
 * 入棧   3 2 4 1 5
 * 最小棧3 2 2 1 1 //同步壓 同步彈 壓入的時候,最小棧每次壓入的是值棧中存在的最小數
 * 
 * 實現方法2 同樣加入一個最小數棧
 * 入棧   3 2 4 1 5
 * 最小棧3 2 1 //壓入的時候,最小棧每次只把小於等於最小棧棧頂的元素同步壓入 彈出時候比較值棧和最小棧棧頂元素是否相同,相同就同步彈出
 * 
 * peek函式返回棧頂元素
 * 
 */
public class Code_02_GetMinStack {
    public static class MyStack1 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack1() {
            this.stackData = new Stack<Integer>();
            this.stackMin = new Stack<Integer>();
        }

        public void push(int newNum) {
            if (this.stackMin.isEmpty()) {
                this.stackMin.push(newNum);
            } else if (newNum <= this.getmin()) {
                this.stackMin.push(newNum);
            }
            this.stackData.push(newNum);
        }

        public int pop() {
            if (this.stackData.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            int value = this.stackData.pop();
            if (value == this.getmin()) {
                this.stackMin.pop();
            }
            return value;
        }

        public int getmin() {
            if (this.stackMin.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            return this.stackMin.peek();
        }
    }

    public static class MyStack2 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack2() {
            this.stackData = new Stack<Integer>();
            this.stackMin = new Stack<Integer>();
        }

        public void push(int newNum) {
            if (this.stackMin.isEmpty()) {
                this.stackMin.push(newNum);
            } else if (newNum < this.getmin()) {
                this.stackMin.push(newNum);
            } else {
                int newMin = this.stackMin.peek();
                this.stackMin.push(newMin);
            }
            this.stackData.push(newNum);
        }

        public int pop() {
            if (this.stackData.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            this.stackMin.pop();
            return this.stackData.pop();
        }

        public int getmin() {
            if (this.stackMin.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            return this.stackMin.peek();
        }
    }

    public static void main(String[] args) {
        MyStack1 stack1 = new MyStack1();
        stack1.push(3);
        System.out.println(stack1.getmin());
        stack1.push(4);
        System.out.println(stack1.getmin());
        stack1.push(1);
        System.out.println(stack1.getmin());
        System.out.println(stack1.pop());
        System.out.println(stack1.getmin());

        System.out.println("=============");

        MyStack1 stack2 = new MyStack1();
        stack2.push(3);
        System.out.println(stack2.getmin());
        stack2.push(4);
        System.out.println(stack2.getmin());
        stack2.push(1);
        System.out.println(stack2.getmin());
        System.out.println(stack2.pop());
        System.out.println(stack2.getmin());
    }

}

題目三 如何僅用佇列結構實現棧結構 如何僅用棧結構實現佇列結構

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class Code_03_StackAndQueueConvert {
    /*
     * 用兩個棧實現佇列
     * 隊A 隊B
     * 隊A 入隊 1 2 3 4 5
     * 隊A出隊填入隊B,保留隊A最後一個元素 
     * 此時 隊A 5
     *    隊B 1 2 3 4
     *    隊A最後一個數彈出
     *    然後隊B填入隊A,隊B保留最後一個元素
     *    隊B最後一個數彈出
     *    然後隊A填入隊B,隊A保留最後一個元素
     *    隊A最後一個數彈出
     */
    public static class TwoStacksQueue {
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;

        public TwoStacksQueue() {
            stackPush = new Stack<Integer>();
            stackPop = new Stack<Integer>();
        }

        public void push(int pushInt) {
            stackPush.push(pushInt);
        }

        public int poll() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.pop();
        }

        public int peek() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.peek();
        }
    }
    /*
     * 用兩個佇列實現棧
     * push棧和pop棧
     * 壓只進push,出只彈pop
     * 原則一 pop棧有東西,push棧不能壓入
     * 原則二 push棧往pop棧壓入的時候,必須一次性把所有資料壓完
     * 只要滿足兩個原則,可以在任意一個時刻判斷和操作
     */
    public static class TwoQueuesStack {
        private Queue<Integer> queue;
        private Queue<Integer> help;

        public TwoQueuesStack() {
            queue = new LinkedList<Integer>();
            help = new LinkedList<Integer>();
        }

        public void push(int pushInt) {
            queue.add(pushInt);
        }

        public int peek() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            help.add(res);
            swap();
            return res;
        }

        public int pop() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            swap();
            return res;
        }

        private void swap() {
            Queue<Integer> tmp = help;
            help = queue;
            queue = tmp;
        }

    }

}

題目四

實現一種狗貓佇列的結構,要求如下:
使用者可以呼叫add方法將cat類或dog類的例項放入佇列中;
使用者可以呼叫pollAll方法,將佇列中所有的例項按照進佇列的先後順序依次彈出;
使用者可以呼叫pollDog方法,將佇列中dog類的例項按照進佇列的先後順序依次彈出;
使用者可以呼叫pollCat方法,將佇列中cat類的例項按照進佇列的先後順序依次彈出;
使用者可以呼叫isEmpty方法,檢查佇列中是否還有dog或cat的例項;
使用者可以呼叫isDogEmpty方法,檢查佇列中是否有dog類的例項;
使用者可以呼叫isCatEmpty方法,檢查佇列中是否有cat類的例項。

public class Pet {
    private String type;
    public Pet(String type) {
    this.type = type;
}
public String getPetType() {
    return this.type;
    }
}
public class Dog extends Pet {
    public Dog() {
    super("dog");
    }
}
public class Cat extends Pet {
    public Cat() {
    super("cat");
    }
}

import java.util.LinkedList;
import java.util.Queue;

public class Code_04_DogCatQueue {

    public static class Pet {
        private String type;

        public Pet(String type) {
            this.type = type;
        }

        public String getPetType() {
            return this.type;
        }
    }

    public static class Dog extends Pet {
        public Dog() {
            super("dog");
        }
    }

    public static class Cat extends Pet {
        public Cat() {
            super("cat");
        }
    }

    public static class PetEnterQueue {
        private Pet pet;
        private long count;

        public PetEnterQueue(Pet pet, long count) {
            this.pet = pet;
            this.count = count;
        }

        public Pet getPet() {
            return this.pet;
        }

        public long getCount() {
            return this.count;
        }

        public String getEnterPetType() {
            return this.pet.getPetType();
        }
    }

    public static class DogCatQueue {
        private Queue<PetEnterQueue> dogQ;
        private Queue<PetEnterQueue> catQ;
        private long count;

        public DogCatQueue() {
            this.dogQ = new LinkedList<PetEnterQueue>();
            this.catQ = new LinkedList<PetEnterQueue>();
            this.count = 0;
        }

        public void add(Pet pet) {
            if (pet.getPetType().equals("dog")) {
                this.dogQ.add(new PetEnterQueue(pet, this.count++));
            } else if (pet.getPetType().equals("cat")) {
                this.catQ.add(new PetEnterQueue(pet, this.count++));
            } else {
                throw new RuntimeException("err, not dog or cat");
            }
        }

        public Pet pollAll() {
            if (!this.dogQ.isEmpty() && !this.catQ.isEmpty()) {
                if (this.dogQ.peek().getCount() < this.catQ.peek().getCount()) {
                    return this.dogQ.poll().getPet();
                } else {
                    return this.catQ.poll().getPet();
                }
            } else if (!this.dogQ.isEmpty()) {
                return this.dogQ.poll().getPet();
            } else if (!this.catQ.isEmpty()) {
                return this.catQ.poll().getPet();
            } else {
                throw new RuntimeException("err, queue is empty!");
            }
        }

        public Dog pollDog() {
            if (!this.isDogQueueEmpty()) {
                return (Dog) this.dogQ.poll().getPet();
            } else {
                throw new RuntimeException("Dog queue is empty!");
            }
        }

        public Cat pollCat() {
            if (!this.isCatQueueEmpty()) {
                return (Cat) this.catQ.poll().getPet();
            } else
                throw new RuntimeException("Cat queue is empty!");
        }

        public boolean isEmpty() {
            return this.dogQ.isEmpty() && this.catQ.isEmpty();
        }

        public boolean isDogQueueEmpty() {
            return this.dogQ.isEmpty();
        }

        public boolean isCatQueueEmpty() {
            return this.catQ.isEmpty();
        }

    }

    public static void main(String[] args) {
        DogCatQueue test = new DogCatQueue();

        Pet dog1 = new Dog();
        Pet cat1 = new Cat();
        Pet dog2 = new Dog();
        Pet cat2 = new Cat();
        Pet dog3 = new Dog();
        Pet cat3 = new Cat();

        test.add(dog1);
        test.add(cat1);
        test.add(dog2);
        test.add(cat2);
        test.add(dog3);
        test.add(cat3);

        test.add(dog1);
        test.add(cat1);
        test.add(dog2);
        test.add(cat2);
        test.add(dog3);
        test.add(cat3);

        test.add(dog1);
        test.add(cat1);
        test.add(dog2);
        test.add(cat2);
        test.add(dog3);
        test.add(cat3);
        while (!test.isDogQueueEmpty()) {
            System.out.println(test.pollDog().getPetType());
        }
        while (!test.isEmpty()) {
            System.out.println(test.pollAll().getPetType());
        }
    }

}

第五題 認識雜湊函式和雜湊表

雜湊表就是散列表
1.多種實現 多種定址
2.經典的雜湊表是陣列+連結串列實現的 在java現在的版本中雜湊表是陣列+紅黑樹(TreeMap實現的)
3.HashMap的CRUD認為都是O(1)的
4.散列表的擴容(每一次原長度*2) 可以離線完成,建好了再算一遍位置,填入 然後舊錶換新表

//hashmap在使用的時候增刪改查都視為時間複雜度O(1)的操作
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

public class Code_05_HashMap {

    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put("zuo", "31");

        System.out.println(map.containsKey("zuo"));
        System.out.println(map.containsKey("chengyun"));
        System.out.println("=========================");

        System.out.println(map.get("zuo"));
        System.out.println(map.get("chengyun"));
        System.out.println("=========================");

        System.out.println(map.isEmpty());
        System.out.println(map.size());
        System.out.println("=========================");

        System.out.println(map.remove("zuo"));
        System.out.println(map.containsKey("zuo"));
        System.out.println(map.get("zuo"));
        System.out.println(map.isEmpty());
        System.out.println(map.size());
        System.out.println("=========================");

        map.put("zuo", "31");
        System.out.println(map.get("zuo"));
        map.put("zuo", "32");
        System.out.println(map.get("zuo"));
        System.out.println("=========================");

        map.put("zuo", "31");
        map.put("cheng", "32");
        map.put("yun", "33");

        for (String key : map.keySet()) {
            System.out.println(key);
        }
        System.out.println("=========================");

        for (String values : map.values()) {
            System.out.println(values);
        }
        System.out.println("=========================");

        map.clear();
        map.put("A", "1");
        map.put("B", "2");
        map.put("C", "3");
        map.put("D", "1");
        map.put("E", "2");
        map.put("F", "3");
        map.put("G", "1");
        map.put("H", "2");
        map.put("I", "3");
        for (Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }
        System.out.println("=========================");

        // you can not remove item in map when you use the iterator of map
        // for(Entry<String,String> entry : map.entrySet()){
        // if(!entry.getValue().equals("1")){
        // map.remove(entry.getKey());
        // }
        // }

        // if you want to remove items, collect them first, then remove them by
        // this way.
        List<String> removeKeys = new ArrayList<String>();
        for (Entry<String, String> entry : map.entrySet()) {
            if (!entry.getValue().equals("1")) {
                removeKeys.add(entry.getKey());
            }
        }
        for (String removeKey : removeKeys) {
            map.remove(removeKey);
        }
        for (Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }
        System.out.println("=========================");

    }

}

題目六 設計RandomPool結構

【題目】
設計一種結構,在該結構中有如下三個功能:
insert(key):將某個key加入到該結構,做到不重複加入。
delete(key):將原本在結構中的某個key移除。
getRandom():等概率隨機返回結構中的任何一個key。
【要求】
Insert、delete和getRandom方法的時間複雜度都是O(1)。

import java.util.HashMap;

public class Code_06_RandomPool {

    public static class Pool<K> {
        private HashMap<K, Integer> keyIndexMap;
        private HashMap<Integer, K> indexKeyMap;
        private int size;

        public Pool() {
            this.keyIndexMap = new HashMap<K, Integer>();
            this.indexKeyMap = new HashMap<Integer, K>();
            this.size = 0;
        }

        public void insert(K key) {
            if (!this.keyIndexMap.containsKey(key)) {
                this.keyIndexMap.put(key, this.size);
                this.indexKeyMap.put(this.size++, key);
            }
        }

        public void delete(K key) {
            if (this.keyIndexMap.containsKey(key)) {
                int deleteIndex = this.keyIndexMap.get(key);
                int lastIndex = --this.size;
                K lastKey = this.indexKeyMap.get(lastIndex);
                this.keyIndexMap.put(lastKey, deleteIndex);
                this.indexKeyMap.put(deleteIndex, lastKey);
                this.keyIndexMap.remove(key);
                this.indexKeyMap.remove(lastIndex);
            }
        }

        public K getRandom() {
            if (this.size == 0) {
                return null;
            }
            int randomIndex = (int) (Math.random() * this.size);
            return this.indexKeyMap.get(randomIndex);
        }

    }

    public static void main(String[] args) {
        Pool<String> pool = new Pool<String>();
        pool.insert("zuo");
        pool.insert("cheng");
        pool.insert("yun");
        System.out.println(pool.getRandom());
        System.out.println(pool.getRandom());
        System.out.println(pool.getRandom());
        System.out.println(pool.getRandom());
        System.out.println(pool.getRandom());
        System.out.println(pool.getRandom());

    }

}

題目七 轉圈列印矩陣

【題目】
給定一個整型矩陣matrix,請按照轉圈的方式列印它。
例如:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
列印結果為:1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,
10
【要求】
列印矩陣

public class Code_07_PrintMatrixSpiralOrder {

    public static void spiralOrderPrint(int[][] matrix) {
        int row1 = 0;
        int col1 = 0;
        int row2 = matrix.length - 1;
        int col2 = matrix[0].length - 1;
        while (row1 <= row2 && col1 <= col2) {//一次一圈
            printEdge(matrix, row1++, col1++, row2--, col2--);
        }
    }

    public static void printEdge(int[][] m, int row1, int col1, int row2, int col2) {
        if (row1 == row2) { //一行的情況
            for (int i = col1; i <= col2; i++) {
                System.out.print(m[row1][i] + " ");
            }
        } else if (col1 == col2) { // 一列的情況
            for (int i = row1; i <= row2; i++) {
                System.out.print(m[i][col1] + " ");
            }
        } else { // 矩陣
            int curC = col1;
            int curR = row1;
            while (curC != col2) {//上下左右四個邊列印完剛好一圈
                System.out.print(m[row1][curC] + " ");
                curC++;
            }
            while (curR != row2) {
                System.out.print(m[curR][col2] + " ");
                curR++;
            }
            while (curC != col1) {
                System.out.print(m[row2][curC] + " ");
                curC--;
            }
            while (curR != row1) {
                System.out.print(m[curR][col1] + " ");
                curR--;
            }
        }
    }

    public static void main(String[] args) {
        int[][] matrix = { 
        { 1, 2, 3, 4 }, 
        { 5, 6, 7, 8 }, 
        { 9, 10, 11, 12 },
        { 13, 14, 15, 16 } };
        spiralOrderPrint(matrix);

    }

}

第八題 “之”字形列印矩陣

【題目】
給定一個矩陣matrix,按照“之”字形的方式列印這個矩陣,例如:
1 2 3 4
5 6 7 8
9 10 11 12
“之”字形列印的結果為:1,2,5,9,6,3,4,7,10,11,8,12
【要求】
額外空間複雜度為O(1)。

跳出如何變換下標的區域性思想
從巨集觀思考
這裡寫圖片描述

public class Code_08_ZigZagPrintMatrix {

    public static void printMatrixZigZag(int[][] matrix) {
        int row1 = 0;
        int col1 = 0;
        int row2 = 0;
        int col2 = 0;
        int endR = matrix.length - 1;
        int endC = matrix[0].length - 1;
        boolean fromUp = false;
        while (row1 != endR + 1) {
            printLevel(matrix, row1, col1, row2, col2, fromUp);
            row1 = col1 == endC ? row1 + 1 : row1;
            col1 = col1 == endC ? col1 : col1 + 1;
            col2 = row2 == endR ? col2 + 1 : col2;
            row2 = row2 == endR ? row2 : row2 + 1;
            fromUp = !fromUp;
        }
        System.out.println();
    }

    public static void printLevel(int[][] m, int row1, int col1, int row2, int col2,
            boolean f) {
        if (f) {
            while (row1 < row2) {//tR != dR + 1
                System.out.print(m[row1++][col1--] + " ");
            }
        } else {
            while (row2 != row1 - 1) {
                System.out.print(m[row2--][col2++] + " ");
            }
        }
    }

    public static void main(String[] args) {
        int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
        printMatrixZigZag(matrix);

    }

}

題目九 在行列都排好序的矩陣中找數

【題目】
給定一個有N*M的整型矩陣matrix和一個整數K,matrix的每一行和每一
列都是排好序的。實現一個函式,判斷K是否在matrix中。
例如:
0 1 2 5
2 3 4 7
4 4 4 8
5 7 7 9
如果K為7,返回true;如果K為6,返回false。
【要求】
時間複雜度為O(N+M),額外空間複雜度為O(1)。

/*
從左上或者右下開始找
m[i][j]>x向左不向下 m[i][j]<x向下越界找不到就是false
水題
 */
public class Code_09_FindNumInSortedMatrix {

    public static boolean isContains(int[][] matrix, int K) {
        int row = 0;
        int col = matrix[0].length - 1;
        while (row < matrix.length && col > -1) {
            if (matrix[row][col] == K) {
                return true;
            } else if (matrix[row][col] > K) {
                col--;
            } else {
                row++;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int[][] matrix = new int[][] { { 0, 1, 2, 3, 4, 5, 6 },// 0
                { 10, 12, 13, 15, 16, 17, 18 },// 1
                { 23, 24, 25, 26, 27, 28, 29 },// 2
                { 44, 45, 46, 47, 48, 49, 50 },// 3
                { 65, 66, 67, 68, 69, 70, 71 },// 4
                { 96, 97, 98, 99, 100, 111, 122 },// 5
                { 166, 176, 186, 187, 190, 195, 200 },// 6
                { 233, 243, 321, 341, 356, 370, 380 } // 7
        };
        int K = 233;
        System.out.println(isContains(matrix, K));
    }

}

題目十 列印兩個有序連結串列的公共部分

【題目】
給定兩個有序連結串列的頭指標head1和head2,列印兩個連結串列的公共部分。

//水題 看程式碼就行
public class Code_10_PrintCommonPart {

    public static class Node {
        public int value;
        public Node next;
        public Node(int data) {
            this.value = data;
        }
    }

    public static void printCommonPart(Node head1, Node head2) {
        System.out.print("Common Part: ");