常用算法(java)

排序

常用算法(java)_第1张图片

1. 冒泡排序(O(n2))

把最大的沉到最后,两两交换

public void bubbleSort(int[] nums) {
//      外层循环控制排序的轮数
        for (int i = nums.length - 1 ; i > 0; i--) {
            boolean isSorted = true;
//            内层循环每次将未排序序列中最大的数冒泡到最后端
            for (int j = 0; j < i;j++) {
//                升序
                if (nums[j] > nums[j+1]) {
                    swap(nums,j,j+1);
                    isSorted = false;
                }
            }
            if (isSorted) {
                break;
            }
        }
    }

 

2. 快速排序(O(nlogn))

哨兵

public void quickSort(int[] nums,int l, int r) {
        if (l < r) {
            int q = partition(nums,l,r);
            quickSort(nums,l,q-1);
            quickSort(nums,q+1,r);
        }
    }

    private int partition(int[] nums, int l, int r) {
//        临界值,哨兵
        int tmp = nums[l];
        int i = l, j = r;
        while (i != j) {
//            都带等号
            while (i < j && tmp <= nums[j]) {
                j--;
            }
            while (i < j && tmp >= nums[i]) {
                i++;
            }

            if (i < j) {
                swap(nums,i,j);
            }
        }
//        将基准数归位,放在中间位置
        nums[l] = nums[i];
        nums[i] = tmp;

        return i;
    }

 

3. 选择排序(O(n2))

每次选出最小的放到端点处

public void selectSort(int[] nums) {
        for (int i = 0 ; i < nums.length; i++) {
            int index = i;
            for (int j = i+1; j < nums.length;j++) {
//                找出最小的,放在端点处
                if (nums[j] < nums[index]) {
                    index = j;
                }
            }
            swap(nums,i,index);
        }
    }

 

4. 插入排序(O(n2))

把当前的数(nums[i])插入到之前已经排好序的数组中

public void insertSort(int[] nums) {
        for (int i = 1; i < nums.length; i++) {
            int tmp = nums[i];
            int j = i;
            while (j > 0 && nums[j-1] > tmp) {
                nums[j] = nums[j-1];
                j--;
            }
            nums[j] = tmp;
        }
    }

 

5. 二分插入排序(O(nlogn))

public void binaryInsertSort(int[] nums) {
        for (int i = 1; i < nums.length; i++) {
            int tmp = nums[i];
            int l = 0, r = i-1;
//        l <= r
            while (l <= r) {
                int mid = l + (r-l)/2;
                if (nums[mid] < tmp) {
                    l = mid + 1;
                }
                else {
                    r = mid - 1;
                }
            }
//            要插入的位置之后的都后移
            for (int j = i - 1;j >= l; j--) {
                nums[j+1] = nums[j];
            }
//            插入
            nums[l] = tmp;
        }
    }

 

 

链表:

1. 判断相交

相同的尾节点

2. 找到交点

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode l1 = pHead1, l2 = pHead2;
        while (l1 != l2) {
            l1 = (l1 == null) ? pHead2 : l1.next;
            l2 = (l2 == null) ? pHead1 : l1.next;
        }
        return l1;
    }

3. 判断环

快慢指针

4. 找到环的入口

public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if (pHead == null || pHead.next == null || pHead.next.next ==null) {
            return null;
        }
        ListNode fast = pHead.next.next;
        ListNode slow = pHead.next;

//        判断有环
        while (slow != fast) {
            if (fast.next != null && fast.next.next != null) {
                fast = fast.next.next;
                slow = slow.next;
            }
            else {
               return null;
            }
        }

        fast = pHead;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        
        return slow;
    }

5. 链表反转

public ListNode ReverseList(ListNode head) {
    ListNode pre = null, next = null, reverseHead = null;
    ListNode pNode = head;

    while (pNode != null) {
        next = pNode.next;
        pNode.next = pre;
        pre = pNode;
        pNode = next;
        if (pNode == null) {
            reverseHead = pre;
        }
    }

    return reverseHead;
}

6. 删除链表中的重复节点

新建一个头结点,防止第一个数就重复;用pre指针保存前一个节点;直接比较pnode.next.val == pnode.val,直到找到最后一个相等的值

public ListNode deleteDuplication(ListNode pHead)
    {
        if (pHead == null || pHead.next == null) {
            return pHead;
        }

        ListNode newHead = new ListNode(-1);
        newHead.next = pHead;
        ListNode pre = newHead;
        ListNode pNode = newHead.next;

        while (pNode != null) {
            if (pNode.next != null && pNode.val == pNode.next.val) {
//                找到最后一个相同的节点
                while (pNode.next != null && pNode.val == pNode.next.val) {
                    pNode = pNode.next;
                }
                pre.next = pNode.next;
                pNode = pNode.next;
            }
            else {
                pre = pre.next;
                pNode = pNode.next;
            }
        }

        return newHead.next;
    }

7. 倒数第k个节点

一个先走k-1步,另一个再开始,第一个到头的时候第二个所在的位置

public ListNode FindKthToTail(ListNode head,int k) {
    if (head == null || k < 0) {
        return null;
    }
    ListNode p1 = head,p2= head;
    for (int i = 0 ; i < k; i++) {
        if (p1 == null ){
            return null;
        }
        p1 = p1.next;
    }

    while (p1 != null) {
        p1 = p1.next;
        p2 = p2.next;
    }

    return p2;
}

8. 合并两个有序链表

public ListNode Merge(ListNode list1,ListNode list2) {
    if (list1 == null) {
        return list2;
    }
    if (list2 == null) {
        return list1;
    }
    if (list1.val <= list2.val) {
        list1.next = Merge(list1.next,list2);
        return list1;
    }
    else {
        list2.next = Merge(list1,list2.next);
        return list2;
    }
}

9. 复制链表

 

 

二叉树:

https://blog.csdn.net/weixin_39795049/article/details/89036538

1. 判断root2是不是root1的子结构

两个递归函数,作用分别是:1找到r1中与r2根节点相同的节点;2再判断r1中以该根为节点的树有没有和r2相同的结构。

public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if (root2 == null) return false;
        boolean result = false;

        if (root1 != null && root2 != null) {
            if (root1.val == root2.val) {
                result = doesRoot1HasRoot2(root1,root2);
            }
            if (!result) {
                result = HasSubtree(root1.left,root2);
            }
            if (!result) {
                result = HasSubtree(root1.right,root2);
            }
        }

        return result;
    }

    private boolean doesRoot1HasRoot2(TreeNode root1, TreeNode root2) {
        if (root2 == null) {
            return true;
        }
        if (root1 == null) {
            return false;
        }
        if (root1.val != root2.val) {
            return false;
        }

        return doesRoot1HasRoot2(root1.left,root2.left) && doesRoot1HasRoot2(root1.right,root2.right);
    }

 

2. 二叉树的镜像

先交换左右节点,再调整树里面的结构

public void Mirror(TreeNode root) {
        if (root == null)
            return;
        if (root.left == null && root.right == null) {
            return;
        }

        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;

        if (root.left != null) {
            Mirror(root.left);
        }
        if (root.right != null) {
            Mirror(root.right);
        }
    }

 

3. 二叉树层序遍历(队列)

public ArrayList PrintFromTopToBottom(TreeNode root) {
        ArrayList list = new ArrayList<>();

        if (root == null) {
            return list;
        }

        Queue queue = new LinkedList<>();

        queue.add(root);
        list.add(root.val);
        boolean flag = false;

        while (!queue.isEmpty()) {
            TreeNode pnode = queue.poll();
            if (flag) {
                list.add(pnode.val);
            }
            if (pnode.left != null) {
                queue.add(pnode.left);
                //list.add(pnode.left.val);
            }
            if (pnode.right != null) {
                queue.add(pnode.right);
                //list.add(pnode.right.val);
            }
            if (!flag) {
                flag = true;
            }
        }
        return list;
}

 

3. BST的后序遍历序列

递归,BST后序,最后一个肯定是根节点。然后遍历找到第一个比根节点大的,就是右子树开始。然后递归,子树也具有相同的性质。

private boolean judge(int[] sequence, int l,int r) {
        int root = sequence[r];
        int i = l;
        for (i = l ;i < r; i++) {
            if (sequence[i] > root) {
                break;
            }
        }
        //此时的i是右子树后序遍历的第一个节点(右子树左下角)

        int j = i;
        for (j = i; j < r ;j++) {
            if (sequence[j] < root) {
                return false;
            }
        }

        boolean left = true;
//        左子树存在
        if (i > l) {
            left = judge(sequence,l,i-1);
        }
        boolean right = true;
//        右子树存在
        if (i < r) {
            right = judge(sequence,i,r-1);
        }

        return left && right;


    }

 

4. 二叉树中和为某一值的序列

注意:序列是从根到叶子节点;按长度排序,使用lamda表达式

ArrayList path = new ArrayList<>();
    ArrayList> list = new ArrayList<>();
 
    public ArrayList> FindPath(TreeNode root,int target) {
        help(root, target);
        Collections.sort(list, (o1,o2)->o2.size()-o1.size());
        return list;
    }
 
    public ArrayList> help(TreeNode root, int target) {
        if (root == null) {
            return list;
        }
 
        target = target - root.val;   //因为任何一个路径都一定包括根节点
        if (target >= 0) {
            path.add(root.val);
 
 
            if (target == 0 && root.left == null && root.right == null) {
                list.add(new ArrayList(path));             //不能是list.add(path);不重新new的话从始至终listAll中所有引用都指向了同一个一个list
            }
 
            FindPath(root.left,target);         //深度遍历,必须先左后右
            FindPath(root.right,target);
 
            path.remove(path.size()-1);     //移除最后一个元素,深度遍历完一条路径之后要回退。因为如果遍历到叶子节点发现此叶子节点不符合,需要将次叶节点从list里删除,回溯到叶节点的父节点。
                                                    //并不是没找到路径要回退,找到了也要回退的。因为一直使用了同一个list的空间,遍历完一条路径后回退去找别的路径一定要从list后面删掉回退的元素,不然list会越来越长。
 
        }
        return list;                    //return list; 这一句在函数里是没有实际意义的,因为返回之后,没有任何操作,而且listAll是全局变量,所以在函数运行的过程中,可以被加入符合要求的路径。
    }
 
 }

 

5. 二叉树与双向链表

public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null) return null;

        if (pRootOfTree.left == null && pRootOfTree.right == null) return pRootOfTree;

        //1. 左子树构成双链表,返回头结点
        TreeNode left = Convert(pRootOfTree.left);
        TreeNode tmp = left;
        //2. 定位至左子树最后一个节点
        while (tmp != null && tmp.right != null) {
            tmp = tmp.right;
        }
        //3. 如果左子树不为空,则将root追加
        if (left != null) {
            tmp.right = pRootOfTree;
            pRootOfTree.left = tmp;
        }
        //4. 将右子树构成双链表
        TreeNode right = Convert(pRootOfTree.right);
        //5. 将右子树链表追加到root之后
        if (right != null) {
            pRootOfTree.right = right;
            right.left = pRootOfTree;
        }

        return left==null ? pRootOfTree : left;
    }

 

6. 判断一棵二叉树是不是对称的

boolean isSymmetrical(TreeNode pRoot)
    {
        if (pRoot == null) {
            return true;
        }
        
        return symmetricalHelper(pRoot.left,pRoot.right);
    }

    private boolean symmetricalHelper(TreeNode left, TreeNode right) {
        if (left == null) return right == null;
        if (right == null) return false;
        if (left.val != right.val) return false;
        return symmetricalHelper(left.left,right.right) && symmetricalHelper(left.right,right.left);
    }

7. 之字形打印二叉树

层序遍历的变种,要每一行遍历顺序反过来(用两个栈,因为每次压进去的时候出栈就会反过来,奇数层的时候其子节点从左至右压入s2,偶数层的时候其子节点从右至左压入s1

public ArrayList> Print(TreeNode pRoot) {
        ArrayList row = new ArrayList<>();
        ArrayList> res = new ArrayList<>();
        if (pRoot == null) {
            return res;
        }

        Stack s1 = new Stack<>();
        Stack s2 = new Stack<>();
        s1.push(pRoot);
        int layer = 1;
        while (!s1.isEmpty() || !s2.isEmpty()) {
            if (layer % 2 != 0) {
                while (!s1.isEmpty()) {
                    TreeNode tmp = s1.pop();
                    row.add(tmp.val);
                    if (tmp.left != null) {
                        s2.push(tmp.left);
                    }
                    if (tmp.right != null) {
                        s2.push(tmp.right);
                    }
                }
            }
            else {
                while (!s2.isEmpty()) {
                    TreeNode tmp = s2.pop();
                    row.add(tmp.val);
                    if (tmp.right != null) {
                        s1.push(tmp.right);
                    }
                    if (tmp.left != null) {
                        s1.push(tmp.left);
                    }
                }
            }
            if (!row.isEmpty()) {
                res.add(row);
                row = new ArrayList<>();
                layer++;
            }
        }
        return res;
    }

 

8. 二叉树的下一个中序遍历节点

public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if (pNode == null) {
            return pNode;
        }

//        如果该节点有右孩子,则一定指向其右孩子的最下面的左孩子
        if (pNode.right != null) {
            TreeLinkNode right = pNode.right;
            while (right.left != null) {
                right = right.left;
            }
            return right;
        }

//        节点不是根节点,如果是父节点的左孩子,则直接返回父节点
        while (pNode.next != null) {
            TreeLinkNode parent = pNode.next;
            if (pNode == parent.left) {
                return parent;
            }
//----------------------------重点----------------------
//            如果是父节点的右孩子,则返回其父节点的父节点,重复判断是不该父节点的左孩子
            pNode = pNode.next;
        }

        return null;

    }

 

9. 层序打印二叉树

ArrayList> Print(TreeNode pRoot) {
        ArrayList row = new ArrayList<>();
        ArrayList> res = new ArrayList<>();

        if (pRoot == null) {
            return res;
        }

        int start = 0, end = 1;
        Queue queue = new LinkedList<>();
        queue.add(pRoot);


        while (!queue.isEmpty()) {
            TreeNode tmp = queue.poll();
            row.add(tmp.val);
            start++;

            if (tmp.left != null) {
                queue.add(tmp.left);
            }
            if (tmp.right != null) {
                queue.add(tmp.right);
            }
            if (start == end) {
                end = queue.size();
                start = 0;
//=================重点=============
                res.add(row);
                row = new ArrayList<>();
            }
        }

        return res;
    }

 

10. 序列化和反序列化二叉树

本来,是前序+中序(或后+中)可以还原一棵二叉树。但是现在,我们对null进行特殊字符处理,直接用前序进行序列化。

String Serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        if (root == null) {
            sb.append("#,");
            return sb.toString();
        }
        sb.append(root.val+",");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
    }

    int start = -1;
    TreeNode Deserialize(String str) {
        start++;
        if (start >= str.length()) {
            return null;
        }
        String[] nodes = str.split(",");

        if (start < nodes.length && !nodes[start].equals("#")) {
            TreeNode pnode = new TreeNode(Integer.valueOf(nodes[start]));
            pnode.left = Deserialize(str);
            pnode.right = Deserialize(str);
            return pnode;
        }

        return null;
    }

11. 二叉搜索树的第k大节点

TreeNode KthNode(TreeNode pRoot, int k)
    {
        if (pRoot == null) {
            return null;
        }

        Stack stack = new Stack<>();
        TreeNode pnode = pRoot;
        int count = 0;

        while (pnode != null || !stack.isEmpty()) {
            if (pnode != null) {
                stack.push(pnode);
                pnode = pnode.left;
            }
//            stack不空
            else {
                TreeNode tmp = stack.pop();
                count++;
                if (count == k) {
                    return tmp;
                }
                pnode = tmp.right;
            }
        }

        return null;
    }

 

 

多线程:

三个线程顺序打印自己的名字一遍(join)

public class TestPrintOrder1 {
	public static void main(String[] args) throws InterruptedException {
		Thread testA = new TestThread("A");
		Thread testB = new TestThread("B");
		Thread testC = new TestThread("C");
		testA.start();
		testA.join();
		testB.start();
		testB.join();
		testC.start();
	}
}
class TestThread extends Thread{
	String name = "";
	public TestThread(String name){
		this.name = name;
	}
	public void run(){
		System.out.print(name);
	}
}

三个线程顺序打印自己的名字十遍(notifyAll)

public class TestPrint {
	static int count = 0;
	static final Object obj = new Object();
	Thread t1 = new Thread(new Runnable() {
		@Override
		public void run() {
			while (true) {
				synchronized (obj) {
					if (count % 3 == 0) {
						System.out.println("A");
						count++;
						obj.notifyAll();
					} else
						try {
							obj.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
				}
			}
		}
	});
	Thread t2 = new Thread(new Runnable() {
		@Override
		public void run() {
			while (true) {
				synchronized (obj) {
					if (count % 3 == 1) {
						System.out.println("B");
						count++;
						obj.notifyAll();
					} else
						try {
							obj.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
				}
			}
		}
	});
	Thread t3 = new Thread(new Runnable() {
		@Override
		public void run() {
			while (true) {
				synchronized (obj) {
					if (count % 3 == 2) {
						System.out.println("C");
						count++;
						obj.notifyAll();
					} else
						try {
							obj.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
				}
			}
		}
	});
 
	public void fun() {
		t3.start();
		t1.start();
		t2.start();
	}
 
	public static void main(String[] args) {
		TestPrint tp = new TestPrint();
		long t1 = System.currentTimeMillis();
		tp.fun();
		while (true) {
			if (System.currentTimeMillis() - t1 >= 10)// 运行10个毫秒
				System.exit(0);
		}
	}

 

常见技巧题:

1. 数组中超过一半的数字(hash、阵地攻守)

2. 连续子数组的最大和(动态规划)

3. 最小元素的栈(辅助栈)

4. 判断是否是栈的弹出序列(辅助栈)

for (int i = 0,j = 0; i < pushA.length; i++) {
            assistStack.push(pushA[i]);
            while (!assistStack.empty() && assistStack.peek() == popA[j]) {
                j++;
                assistStack.pop();
            }
        }

        if (assistStack.empty()) {
            isOrder = true;
        }

5. 和为S的两个数字

双指针/hashMap(map.put(arr[i],i),判断map.containsKey(sum-arr[i]))&& i != map.get(sum-arr[i]))

 

6. 第k大的数/最小的k个数

1) 快排思想,只用partition,不用sort。如果剩下的一半个数==k,就直接输出(O(n))

2)如果不能改变原数组,使用堆,java中priorityQueue是根据小顶堆实现的。改成有k个数的大顶堆,然后每次发现比堆顶小的就移除堆顶,加入进去。(O(nlogk))

 

7. 第一个只出现一次的字符

两次HashMap,分别保存下标和出现的次数。其中,countMap使用LinkedHashMap,保证存取顺序相同,就可以直接取出是第一次出现的元素

 

8. 数字在排序数组中出现的次数(HashMap)

如果是有序数组,二分!!

二分,分别找到第一次出现和最后一次出现的位置。左右0.5,找到该插入的地方即可。

 

9. 数组中除了两个数出现了一次之外剩下的都出现了两次

两次异或,第一次的结果是两个出现一次的数的异或结果;第二次先找到这个值中第一位为1的,表示这两个数中这一位一个为0一个为1,所以可以把整个数组分为两组,再进行异或,每一组中只有一个数出现了一次,就分别找到了这两个数。

 

10. 和为S的正整数序列

滑动窗口

 

11. 两个数交换不使用第三个变量

两种方法:

1. a=b-a; b=b-a; a=b+a; 

2. a=a^b; b=a^b; a=a^b; 

 

12. 左旋字符串

三次旋转(YX = (XTYT)T)(先X,再Y,然后全部)

同理,右旋字符串,右旋n = 左旋len-n,所以还是可以这样写。

 

13. 旋转字符串

“student. a am I”。这种,即 单词内部是正确的,但是单词之间倒过来了。

两次旋转,先转全部,再转每个单词内部

 

14. 每次喊到m-1的退出(约瑟夫环?)

bt = 0;
bt = (bt + m - 1) % list.size();

15. 1+2+3..+n不用while、if等

用&&的短路性质实现递归的退出(短路就是如果前面是false后面就不执行)

boolean ans = (n > 0) && (sum += Sum_Solution(n-1))>0;

16. 不用加减乘除做加法

加法:异或操作^;进位:与操作&再左移一位。

按照十进制的思路来,例如5+7
二进制就是101+111,先算加法(不带进位)即101^111=010;再算进位(纯进位)(101&111)<<1 = 1010;
再重复上面的(此时num1=加法结果010,num2=进位结果1010)直到进位为0

 

17. 数组中重复的数字(长度为n,大小都在0~n-1中)

1) 布尔数组;

2)归位,将numers[i]放到numbers[numbers[i]]的位置上,(即将k放到第k位),如果第k位本身就是k,则就重复;

3)遇到numbers[i]就将numbers[numbers[i]]上的数+n,因为本来都是0~n-1之间的数字,所以再下次遇到本来就比n大的数字就断定该位重复。

 

18. 构建乘积数组

B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1],不能使用除法

上下三角分开计算,可以重复利用。(先计算下三角,再上三角)

 

19. 正则表达式匹配

递归,正向思路(比较容易想,主要是有*的情况);

动态规划,逆向思路

 

20. 运用正则表达式(比如判断字符串是不是数字)

[]表示匹配任意一个;?表示0/1个,+表示1/多个,*表示0/多个,^表示以其开头,$表示以其结尾。

 

21. 排序数组去重(O(n))

快慢指针,一个从0,一个从1,

for (int j = 1; j < nums.length; j++) {
        if (nums[j] != nums[i]) {
            i++;
            nums[i] = nums[j];
        }
    }
    return i + 1;

22. 洗牌算法

就是随机打乱,每个数都不在之前的位置上

从len-1开始往前,

  1. tempInd=rdm.nextInt(i);

  2. this.swap(curList[i], curList[tempInd]);

 

23. 滑动窗口最大值

双端队列,用一个双端队列保存当前窗口的最大值的下标

保证队列首元素为当前窗口最大值下标

for (int i = 0; i < num.length; i++) {
//            来了一个新的值,队列中保存的值一定是没有过期的,所以就要和队尾的元素比较,清除队列中所有比当前元素小的值
//            且队首元素时最大的,所以可以优化一下,如果比队首元素还大,就直接clear
            if (!queue.isEmpty() && num[i] > num[queue.peekFirst()]) {
                queue.clear();
            }
            else {
                while (!queue.isEmpty() && num[queue.peekLast()] < num[i]) {
                    queue.removeLast();
                }
            }
            queue.add(i);
//            判断过期
            if (queue.peekFirst() <= i - size) {
                queue.pollFirst();
            }
//            当滑动窗口首地址i大于等于size时才开始加入集合
            if (i >= size - 1) {
                list.add(num[queue.peekFirst()]);
            }
        }

 

24. 最长公共子串

动态规划,然后遍历选出最大的

        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = 0;
                }
        }

 

25. 矩阵中的路径

回溯,DFS,visited

int[][] direction = {{0,-1},{1,0},{0,1},{-1,0}};
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        if (matrix == null || matrix.length <= 0 || str == null || cols <= 0 || rows <= 0) {
            return false;
        }

        boolean[][] visited = new boolean[rows][cols];

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (dfs(0,matrix,rows,cols,i,j,str,visited)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int curLen, char[] matrix, int rows, int cols, int i, int j, char[] str, boolean[][] visited) {
        if (curLen == str.length) {
            return true;
        }
        if (i < 0 || i >= rows || j < 0 || j >= cols || visited[i][j] || i*cols+j < 0 || matrix[i*cols+j] != str[curLen]) {
            return false;
        }
        visited[i][j] = true;
        for (int[] d : direction) {
            if (dfs(curLen+1,matrix,rows,cols,i+d[0],j+d[1],str,visited)) {
                return true;
            }
        }

        visited[i][j] = false;

        return false;
    }

26. 机器人运动路径

不带回溯的dfs,和上一题的区别就是不用在最后归位visited

27. 数据流中的中位数

双堆。大顶堆存放较小的数;小顶堆存放较大的数。

要保证两个堆差不多平衡,所以可以先进大顶堆,然后取出堆顶(最大的)进入最小堆。

首先要保证数据平均分配到两个堆中,还要保证所有最大堆中的数据要小于最小堆中的数据

28. 反转字符串

要不就是sb.reverse()

或者 前后头尾交换,i

29. 查找由连续数字组成的数组中缺失的数

等差数列求和减去现在的和

30. 实现环形队列

判空:return rear == front;

入队(rear):

   判满:if((rear +1) % MAXSIZE == front) { return false; }

   arr[rear] = value;

   rear = (rear + 1) % MAXSIZE; 

   return true;

出队(front):

   判空

   Object obj = a[front];

   front = (front+1)%a.length;

31. 单链表排序

归并   https://www.bbsmax.com/A/WpdK7ljodV/

32. 三数之和为0

https://www.cnblogs.com/gzshan/p/11127130.html

https://blog.csdn.net/weixin_39784818/article/details/93712318?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2

1)排序+hash

2)排序+双指针(去重)

 

你可能感兴趣的:(刷题,面试)