数据结构和算法基础(一)

文章目录

趁空闲时间刷一遍极客时间上王争的《数据结构与算法之美》课程,个人觉得写的很好,每章节由浅入深且从基础到引入设计类问题,如果写过很多代码想要进行架构设计转型时再回头看这些基础知识还蛮有趣的,以下纪录下随着课程走的部分实现代码和思考;
内容主要是笔记和代码,注:手写一遍代码是有必要的;

链表反转

单链表反转

java 复制代码
class ListNode {  
    int val;  
    ListNode next;  
  
    ListNode(int val) {  
        this.val = val;  
        this.next = null;  
    }  
public ListNode reverseList(ListNode head) {  
        ListNode prev = null;  
        ListNode curr = head;  
  
        while (curr != null) {  
            ListNode nextTemp = curr.next;  // 临时保存下一个节点  
            curr.next = prev;               // 反转当前节点的指针  
            prev = curr;                    // 将前一个节点移动到当前节点  
            curr = nextTemp;                // 将当前节点移动到下一个节点  
        }  
  
        return prev;  // prev 最后会指向新的头节点  
    }  
}

链表合并

两个有序的链表合并,用到了哨兵dummy这个指针记录

java 复制代码
class ListNode {  
    int val;  
    ListNode next;  
      ListNode(int val) {  
        this.val = val;  
        this.next = null;  
    }  
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {  
        // 创建一个哨兵节点,方便处理边界情况  
        ListNode dummy = new ListNode(0);  
        ListNode curr = dummy;  
  
        // 使用两个指针分别遍历两个链表  
        while (l1 != null && l2 != null) {  
            if (l1.val <= l2.val) {  
                curr.next = l1;  
                l1 = l1.next;  
            } else {  
                curr.next = l2;  
                l2 = l2.next;  
            }  
            curr = curr.next;  
        }  
  
        // 处理剩余节点(只能有一个链表还有剩余节点)  
        if (l1 != null) {  
            curr.next = l1;  
        } else {  
            curr.next = l2;  
        }  
    }
}

删除链表倒数第 n 个结点

使用快慢指针,快慢指针在解很多链表题目中都有体现

java 复制代码
class ListNode {  
    int val;  
    ListNode next;    
    ListNode(int val) {  
        this.val = val;  
        this.next = null;  
    }  
    public ListNode removeNthFromEnd(ListNode head, int n) {  
        // 创建一个哨兵节点,简化头节点被删除的情况  
        ListNode dummy = new ListNode(0);  
        dummy.next = head;
        // 初始化快慢指针  
        ListNode fast = dummy;  
        ListNode slow = dummy;  
          // 先将快指针向前移动 n+1 步  
        for (int i = 0; i <= n; i++) {  
            fast = fast.next;  
        }    
        // 然后同时移动快慢指针,直到快指针到达链表末尾  
        while (fast != null) {  
            fast = fast.next;  
            slow = slow.next;  
        }    
        // 此时慢指针指向的节点的下一个节点就是要删除的节点  
        slow.next = slow.next.next;    
        // 返回头节点(注意是哨兵节点的下一个节点)  
        return dummy.next;  
    }    
}

找链表的中间结点

使用快慢指针来实现,快指针每次移动2步,而慢指针每次移动1步。当快指针到达链表末尾时,慢指针将恰好位于链表的中间。

java 复制代码
class ListNode {  
    int val;  
    ListNode next;  
  
    ListNode(int val) {  
        this.val = val;  
        this.next = null;  
    }  
 
      public ListNode findMiddle(ListNode head) {  
        // 初始化快慢指针  
        ListNode slow = head;  
        ListNode fast = head;  
          // 快指针每次移动两步,慢指针每次移动一步  
        while (fast != null && fast.next != null) {  
            slow = slow.next;  // 慢指针移动一步  
            fast = fast.next.next;  // 快指针移动两步  
        }  
  
        // 当快指针到达链表末尾时,慢指针指向中间节点  
        return slow;  
    } 
}

链表中环的检测

快慢指针进行遍历,如果快慢指针不相遇说明没有环

java 复制代码
class ListNode {  
    int val;  
    ListNode next;
    ListNode(int val) {  
        this.val = val;  
        this.next = null;  
    }  

    public boolean hasCycle(ListNode head) {  
        if (head == null || head.next == null) {  
            // 如果链表为空或只有一个节点,则不可能有环  
            return false;  
        }   
        ListNode slow = head;  
        ListNode fast = head;
        // 快慢指针开始移动,直到它们相遇或快指针到达链表末尾  
        while (fast != null && fast.next != null) {  
            slow = slow.next;          // 慢指针每次移动一步  
            fast = fast.next.next;     // 快指针每次移动两步  
  
            // 如果快慢指针相遇,说明链表中存在环  
            if (slow == fast) {  
                return true;  
            }  
        }
        // 快指针到达链表末尾,说明链表中没有环  
        return false;  
    }  
}

排序算法

常用的冒泡、选择、插入、归并、快速算法,手写很重要,写出来会发现即使是一个小的改动对于程序的消耗来说都有所差别;

关于排序的算法还可以参照:https://mp.weixin.qq.com/s/HQg3BzzQfJXcWyltsgOfCQ

在要求高效的很多基础框架代码中都是用了快速排序(递归思路)

java 复制代码
// 冒泡排序  
void bubbleSort(int[] arr) {  
    int n = arr.length;  
    for (int i = 0; i < n - 1; i++) {  
        for (int j = 0; j < n - i - 1; j++) {  
            if (arr[j] > arr[j + 1]) {  
                // 交换arr[j]和arr[j + 1]  
                int temp = arr[j];  
                arr[j] = arr[j + 1];  
                arr[j + 1] = temp;  
            }  
        }  
    }  
}  
  
// 选择排序  
void selectionSort(int[] arr) {  
    int n = arr.length;  
    for (int i = 0; i < n - 1; i++) {  
        int minIdx = i;  
        for (int j = i + 1; j < n; j++) {  
            if (arr[j] < arr[minIdx]) {  
                minIdx = j;  
            }  
        }  
        // 交换arr[i]和arr[minIdx]  
        int temp = arr[minIdx];  
        arr[minIdx] = arr[i];  
        arr[i] = temp;  
    }  
}  
  
// 插入排序  
void insertionSort(int[] arr) {  
    int n = arr.length;  
    for (int i = 1; i < n; i++) {  
        int key = arr[i];  
        int j = i - 1;  
        // 将arr[i]插入到已排序部分arr[0..i-1]  
        while (j >= 0 && arr[j] > key) {  
            arr[j + 1] = arr[j];  
            j = j - 1;  
        }  
        arr[j + 1] = key;  
    }  
} 
// 归并排序  
void mergeSort(int[] arr, int left, int right) {  
    if (left < right) {  
        int mid = left + (right - left) / 2;  
        // 递归排序两个子数组  
        mergeSort(arr, left, mid);  
        mergeSort(arr, mid + 1, right);  
        // 合并两个已排序的子数组  
        merge(arr, left, mid, right);  
    }  
}  
void merge(int[] arr, int left, int mid, int right) {  
    int n1 = mid - left + 1;  
    int n2 = right - mid;  
    int[] L = new int[n1];  
    int[] R = new int[n2];  
    for (int i = 0; i < n1; i++) L[i] = arr[left + i];  
    for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];  
    int i = 0, j = 0;  
    int k = left;  
    while (i < n1 && j < n2) {  
        if (L[i] <= R[j]) {  
            arr[k] = L[i];  
            i++;  
        } else {  
            arr[k] = R[j];  
            j++;  
        }  
        k++;  
    }  
    while (i < n1) {  
        arr[k] = L[i];  
        i++;  
        k++;  
    }  
    while (j < n2) {  
        arr[k] = R[j];  
        j++;  
        k++;  
    }  
}    
// 快速排序  
void quickSort(int[] arr, int low, int high) {  
    if (low < high) {  
        int pi = partition(arr, low, high);  
        // 递归排序两个子数组  
        quickSort(arr, low, pi - 1);  
        quickSort(arr, pi + 1, high);  
    }  
}  
int partition(int[] arr, int low, int high) {  
    int pivot = arr[high];  
    int i = (low - 1);  
    for (int j = low; j < high; j++) {  
        if (arr[j] < pivot) {  
            i++;  
            // 交换arr[i]和arr[j]  
            int temp = arr[i];  
            arr[i] = arr[j];  
            arr[j] = temp;  
        }  
    }  
    // 交换arr[i + 1]和arr[high] (或pivot)  
    int temp = arr[i + 1];  
    arr[i + 1] = arr[high];  
    arr[high] = temp;  
    return i + 1;  
}

递归

递归是一种分治的思维,不适合人类大脑但天然是计算机的处理方式,人类大脑总是想把事情的步骤想的很清晰,12345每一步骤做什么,但是计算机不是这样的;

递归也存在堆栈溢出和重复计算的问题,专栏中也给了对应的方式,重复计算可以通过缓存来解决;

java 复制代码
// 上楼梯问题中可以适当增加缓存来消除重复计算
public int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  
  // hasSolvedList 可以理解成一个 Map,key 是 n,value 是 f(n)
  if (hasSolvedList.containsKey(n)) {
    return hasSovledList.get(n);
  }
  
  int ret = f(n-1) + f(n-2);
  hasSovledList.put(n, ret);
  return ret;
}
相关推荐
大母猴啃编程14 分钟前
C++基础---类和对象(上)
c语言·开发语言·数据结构·c++·学习·算法·青少年编程
ganjiee000723 分钟前
力扣(leetcode)每日一题 2376 统计特殊整数 | 数位dp | 暴力递归+记忆化搜索
算法·leetcode
2301_804774491 小时前
算法学习1
java·数据结构·算法
大神薯条老师1 小时前
Python从入门到高手4.1节-掌握条件控制语句
开发语言·python·算法·机器学习·数据分析
极客小张2 小时前
基于STM32和FPGA的射频数据采集系统设计流程
c语言·stm32·物联网·算法·fpga开发·毕业设计·嵌入式
予早2 小时前
LeetCode 136. 只出现一次的数字
算法·leetcode·职场和发展
luthane2 小时前
python 实现gradient boosting regressor梯度增强回归器算法
python·算法·boosting
程序员阿鹏3 小时前
HashMap为什么线程不安全?如何实现线程安全
java·开发语言·数据结构·安全·eclipse·intellij-idea
sp_fyf_20243 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-30
人工智能·算法·计算机视觉·语言模型·自然语言处理
猫毛已经快要掉光的小猫3 小时前
【算法】leetcode热题100 146.LRU缓存.&& container/list用法
算法·leetcode·缓存·go