日常学习算法总结

    技术2022-07-11  101

    一、基本数据结构之数组

    自定义数组(类似ArrayList),数组必须存在在连续的地址空间,实现数组的增删操作。

    public class CustomArray { private int[] array; // 元素个数 private int size; private static final String TAG = "CustomArray"; public CustomArray(int capacity) { array = new int[capacity]; size = 0; } public void insert(int index, int element) { // 判断下标是否合法 if (index < 0 || index > size) { throw new ArrayIndexOutOfBoundsException("数组越界,超出已有元素范围"); } // 如果实际元素个数达到数组容量的上限,则进行扩容 if (size >= array.length) { LogUtils.d(TAG, "我扩容了"); expansionArray(); } // 数组在内存中占有连续的内存空间,数组插入一个元素,插入位置后面的元素需要依次像后移动一位 /*for (int i = size - 1; i >= index; i--) { array[i + 1] = array[i]; }*/ if (size - 1 - index >= 0) System.arraycopy(array, index, array, index + 1, size - 1 - index); array[index] = element; size++; } public int delete(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("数组越界,超出已有元素范围"); } int delElement = array[index]; // 需要将元素向前移动一位 /*for (int i = index; i < size - 1; i++) { array[i] = array[i + 1]; }*/ // 下面这种方式其实是牺牲空间复杂度来提升时间复杂度 if (size - 1 - index >= 0) System.arraycopy(array, index + 1, array, index, size - 1 - index); size--; return delElement; } public int get(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("数组越界,超出已有元素范围"); } return array[index]; } public void set(int index, int element) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("数组越界,超出已有元素范围"); } array[index] = element; } private void expansionArray() { // 进行两倍扩容 int[] newArray = new int[array.length * 2]; System.arraycopy(array, 0, newArray, 0, array.length); array = newArray; } public void outArray() { if (array == null) { return; } for (int element : array) { LogUtils.d(TAG, "数组元素:" + element); } } public int[] getArray() { return array; } public int getSize() { return size; } }
    二、基本数据结构之链表

    自定义链表(类似LinkedList),实现增删操作

    2.1、定义节点类

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

    2.2、实现链表

    public class CustomLinkedList { // 头结点指针 private Node head; // 尾节点指针 private Node last; // 链表长度 private int size; public void insert(int index, int element) { if (index < 0 || index > size) { throw new IndexOutOfBoundsException("链表越界,超出链表实际范围"); } Node insertNode = new Node(element); if (size == 0) { // 链表长度为0,空链表 head = insertNode; last = insertNode; } else if (index == 0) { // 插入头部 insertNode.next = head; head = insertNode; } else if (index == size) { // 插入尾部 last.next = insertNode; last = insertNode; } else { // 插入中间 // 获取前一个节点 Node preNode = get(index - 1); Node nextNode = preNode.next; preNode.next = insertNode; insertNode.next = nextNode; } size++; } public Node remove(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("链表越界,超出链表实际范围"); } Node removeNode = null; if (index == 0) { // 删除头部 removeNode = head; head = head.next; } else if (index == size - 1) { // 删除尾节点 Node preNode = get(index - 1); removeNode = preNode.next; preNode.next = null; last = preNode; } else { // 删除中间节点 Node preNode = get(index - 1); Node nextNode = preNode.next.next; removeNode = preNode.next; preNode.next = nextNode; } size--; return removeNode; } /** * 查询 * * @param index * @return */ public Node get(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("链表越界,超出链表实际范围"); } Node tempNode = head; for (int i = 0; i < index; i++) { tempNode = tempNode.next; } return tempNode; } public String outPut() { StringBuilder builder = new StringBuilder(); Node tempNode = head; while (tempNode != null) { builder.append(tempNode.data).append(" "); tempNode = tempNode.next; } return builder.toString(); } public Node getNode() { return head; } public String outNode(Node node) { StringBuilder builder = new StringBuilder(); Node tempNode = node; while (tempNode != null) { builder.append(tempNode.data).append(" "); tempNode = tempNode.next; } return builder.toString(); } }
    三、反转链表

    刚好利用上诉我们自定义的链表实现链表反转

    /** * 反转链表 * @param node * @return */ public Node reverseLinkedList(Node node) { // 当前节点,将传递进来的头结点赋值给当前节点 Node curNode = node; // 下一个节点 Node nextNode; // 前一个节点 Node preNode = null; while (curNode != null) { // 取出当前节点的下一个节点赋值给我们定义好的nextNode nextNode = curNode.next; // 将当前节点指向前一个节点实现反转 curNode.next = preNode; // 将当前节点赋值给前一节点 preNode = curNode; // 移动当前节点到下一个节点 curNode = nextNode; } return preNode; }

    调用:

    val cusLinkedList = CustomLinkedList() cusLinkedList.insert(0, 0) cusLinkedList.insert(1, 1) cusLinkedList.insert(2, 2) cusLinkedList.insert(3, 3) cusLinkedList.insert(4, 4) cusLinkedList.insert(5, 5) cusLinkedList.insert(6, 6) cusLinkedList.insert(7, 7) cusLinkedList.insert(8, 8) val node = reverseLinkedList(cusLinkedList.node) val nodeStr = cusLinkedList.outNode(node) LogUtils.d(TAG, "反转链表: $nodeStr")

    结果:

    TestAlgorithmFragment==>: 反转链表: 8 7 6 5 4 3 2 1 0
    四、获取数组中出现次数最多的元素

    4.1、双层for循环

    /** * 获取数组中出现最多的元素和次数 * * @param arr * @return */ public int getArrayMostFrequent(int[] arr) { int mostElement = 0; int tempCount = 0; int timeCount = 0; for (int i = 0; i < arr.length; i++) { tempCount = 0; for (int j = 0; j < arr.length; j++) { if (arr[i] == arr[j]) { tempCount++; } } if (tempCount > timeCount) { timeCount = tempCount; mostElement = arr[i]; } } LogUtils.d(TAG, "出现次数最多的元素: " + mostElement + "==>次数: " + timeCount); return timeCount; }

    4.2、利用map集合,将当前元素作为key,出现次数作为value,然后遍历map集合,value值最大的元素就是出现次数最多的元素

    /** * 获取数组中出现最多的元素和次数 * 使用map优化 * * @param arr * @return */ public int getArrayMostFrequentForMap(int[] arr) { Map<Integer, Integer> map = new HashMap<>(); int mostElement = 0; int count = 0; int timeCount = 0; for (int i = 0; i < arr.length; i++) { if (map.containsKey(arr[i])) { map.put(arr[i], map.get(arr[i]) + 1); } else { map.put(arr[i], 1); } } for (Integer key : map.keySet()) { if (map.get(key) > timeCount) { timeCount = map.get(key); mostElement = key; } count++; } LogUtils.d(TAG + "使用Map优化", "出现次数最多的元素: " + mostElement + "==>最多元素次数: " + timeCount + "元素合并之后遍历次数: " + count); return timeCount; }
    五、有效的字母异位词(leetcode第242题)

    给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

    示例1:

    输入: s = “anagram”, t = “nagaram” 输出: true

    示例2:

    输入: s = “rat”, t = “car” 输出: false

    说明: 你可以假设字符串只包含小写字母。

    思路:就是统计两个字母字符串中拥有的字母个数是否相同 1.可以利用两个长度都为 26 的字符数组来统计每个字符串中小写字母出现的次数,然后再对比是否相等; 2.可以只利用一个长度为 26 的字符数组,将出现在字符串 s 里的字符个数加 1,而出现在字符串 t 里的字符个数减 1,最后判断每个小写字母的个数是否都为 0。

    解题:

    private boolean isAnagram(String s, String t) { // 第一种方式 /*if (s.length() != t.length()) { return false; } int[] arr1 = new int[26]; int[] arr2 = new int[26]; for (int i = 0; i < s.length(); i++) { arr1[s.charAt(i) - 'a']++; arr2[t.charAt(i) - 'a']++; } for (int i = 0; i < arr1.length; i++) { if (arr1[i] != arr2[i]) { return false; } } return true;*/ // 第二种方式 if (s.length() != t.length()) { return false; } int[] arr = new int[26]; for (int i = 0; i < s.length(); i++) { arr[s.charAt(i) - 'a']++; arr[t.charAt(i) - 'a']--; } for (int count : arr) { if (count != 0) { return false; } } return true; /*if (s.length() != t.length()) { return false; } int[] arr = new int[26]; for (int i = 0; i < s.length(); i++) { arr[s.charAt(i) - 'a']++; } for (int i = 0; i < t.length(); i++) { arr[t.charAt(i) - 'a']--; if (arr[t.charAt(i) - 'a'] < 0) { return false; } } return true;*/ // 使用Java自带的高级函数 /*if (s.length() != t.length()) { return false; } Map<Character, Integer> table = new HashMap<Character, Integer>(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); table.put(ch, table.getOrDefault(ch, 0) + 1); } for (int i = 0; i < t.length(); i++) { char ch = t.charAt(i); table.put(ch, table.getOrDefault(ch, 0) - 1); if (table.get(ch) < 0) { return false; } } return true;*/ /*if (s.length() != t.length()) { return false; } char[] str1 = s.toCharArray(); char[] str2 = t.toCharArray(); Arrays.sort(str1); Arrays.sort(str2); return Arrays.equals(str1, str2);*/ }
    六、K 个一组翻转链表(leetcode 25题)

    给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表 k 是一个正整数,它的值小于或等于链表的长度。 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

    示例: 给你这个链表:1->2->3->4->5 当 k = 2 时,应当返回: 2->1->4->3->5 当 k = 3 时,应当返回: 3->2->1->4->5

    说明: 你的算法只能使用常数的额外空间。 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

    迭代法

    /** * k个一组翻转链表 * 1、迭代法 * 2、递归 * @param head * @param k * @return */ public ListNode reverseKGroup(ListNode head, int k) { // 迭代 // 构建一个边界节点 ListNode virtualNode = new ListNode(0); virtualNode.next = head; // 头结点的前一个节点 ListNode pre = virtualNode; while (head != null) { // 构建尾节点 ListNode tail = pre; // 查看剩余部分长度是否大于等于 k for (int i = 0; i < k; i++) { tail = tail.next; if (tail == null) { return virtualNode.next; } } // 下一个k组的头结点 ListNode next = tail.next; // 翻转head -- tail之间的数据 ListNode[] reverseArr = reverse(head, tail); // 重新给头、尾赋值,将翻转之前的尾赋值给头,反之亦然 head = reverseArr[0]; tail = reverseArr[1]; // 把子链表重新接回原链表 pre.next = head; tail.next = next; pre = tail; head = tail.next; } return virtualNode.next; } private ListNode[] reverse(ListNode head, ListNode tail) { ListNode pre = tail.next; ListNode cur = head; ListNode next; while (pre != tail) { next = cur.next; cur.next = pre; pre = cur; cur = next; } return new ListNode[]{tail, head}; }

    迭代法

    public ListNode reverseKGroup1(ListNode head, int k) { // 迭代 // 构建一个边界节点 ListNode virtualNode = new ListNode(0); virtualNode.next = head; // 头结点的前一个节点 ListNode pre = virtualNode; // 构建尾节点 ListNode tail = virtualNode; while (tail.next != null) { // 找到尾节点 for (int i = 0; i < k && tail != null; i++) { tail = tail.next; } if (tail == null) { break; } // 每次循环都需要对当前节点和下一组的开始节点 ListNode cur = pre.next; ListNode next = tail.next; tail.next = null; // 将翻转之后的子链表重新连接到总的链表中 pre.next = reverse(cur); cur.next = next; // 移动pre节点和尾节点 pre = cur; tail = pre; } return virtualNode.next; } /** * 翻转链表 * @param head * @return */ private ListNode reverse(ListNode head) { ListNode cur = head; ListNode pre = null; ListNode next; while (cur != null) { next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; }
    七、两两交换链表中的节点(leetcode 24题)

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

    示例: 输入:head = [1,2,3,4] 输出:[2,1,4,3]

    /** * 两两交换链表中的节点 * 1、迭代法 * 2、递归 * @param head * @return */ public ListNode swapPairs(ListNode head) { // 迭代 /*ListNode virtualNode = new ListNode(0); virtualNode.next = head; ListNode pre = virtualNode; // 两两交换至少要有两个才能进入交换逻辑 while (pre.next != null && pre.next.next != null) { // 赋值两个临时变量,代表交换的两个节点 ListNode one = pre.next; ListNode two = pre.next.next; // 节点交换 pre.next = two; one.next = two.next; two.next = one; pre = one; } return virtualNode.next;*/ // 递归 if (head == null || head.next == null) { return head; } ListNode next = head.next; head.next = swapPairs(next.next); next.next = head; return next; }
    八、有效的括号(leetcode 20题)

    给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

    有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。

    示例 2:

    输入:s = “()[]{}” 输出:true

    代码编写::

    public boolean isValid(String s) { char[] chars = s.toCharArray(); if (chars.length == 0) return true; Map<Character, Character> map = new HashMap<>(); map.put(')', '('); map.put(']', '['); map.put('}', '{'); Stack<Character> stack = new Stack<>(); for (char aChar : chars) { if (!isContains(aChar)) { return false; } if (aChar == '(' || aChar == '[' || aChar == '{') { stack.push(aChar); } else if (!stack.empty() && map.get(aChar) == stack.peek()) { stack.pop(); } else { return false; } } return stack.empty(); } private boolean isContains(char c) { List<Character> list = new ArrayList<>(); list.add('('); list.add(')'); list.add('['); list.add(']'); list.add('{'); list.add('}'); return list.contains(c); }

    简单优化之后:

    /** * 括号的有效性 * * @param s * @return */ public boolean isValid(String s) { char[] chars = s.toCharArray(); if (chars.length == 0) return true; if (chars.length % 2 != 0) return false; /*Map<Character, Character> map = new HashMap<>(); map.put(')', '('); map.put(']', '['); map.put('}', '{');*/ Stack<Character> stack = new Stack<>(); for (char aChar : chars) { if (!isContains(aChar)) { return false; } if (aChar == '(' || aChar == '[' || aChar == '{') { stack.push(aChar); } else if (!stack.empty() && getBracket(aChar) == stack.peek() /*map.get(aChar) == stack.peek()*/) { stack.pop(); } else { return false; } } return stack.empty(); } private char getBracket(char bracket) { if (bracket == ')') { return '('; } else if (bracket == ']') { return '['; } else if (bracket == '}') { return '{'; } else { return ' '; } } private boolean isContains(char c) { return c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}'; }
    Processed: 0.010, SQL: 9