力扣算法题:矩阵(玄幻不变量法),链表(虚拟头节点,递归法)

avatar
作者
猴君
阅读量:0

20240725

一、矩阵

54.螺旋矩阵(循环不变量)

在这里插入图片描述

一个矩阵,循环遍历出来,就得按照顺序去遍历,然后插到我们定义的矩阵里面去
在这里插入图片描述

class Solution {     public int[][] generateMatrix(int n) {         int[][] nums = new int[n][n];//定义一个矩阵         int startX = 0, startY = 0;  // 每一圈的起始点         int offset = 1;         int count = 1;  // 矩阵中需要填写的数字         int loop = 1; // 记录当前的圈数         int i, j; // j 代表列, i 代表行;          while (loop <= n / 2) {              // 顶部             // 左闭右开,所以判断循环结束时, j 不能等于 n - offset             for (j = startY; j < n - offset; j++) {                 nums[startX][j] = count++;             }              // 右列             // 左闭右开,所以判断循环结束时, i 不能等于 n - offset             for (i = startX; i < n - offset; i++) {                 nums[i][j] = count++;             }              // 底部             // 左闭右开,所以判断循环结束时, j != startY             for (; j > startY; j--) {                 nums[i][j] = count++;             }              // 左列             // 左闭右开,所以判断循环结束时, i != startX             for (; i > startX; i--) {                 nums[i][j] = count++;             }             startX++;             startY++;             offset++;             loop++;         }         if (n % 2 == 1) { // n 为奇数时,单独处理矩阵中心的值             nums[startX][startY] = count;         }         return nums;     } }  

二、链表

1 移除链表元素

1.1 原链表删除元素:

/**  * 不添加虚拟节点and pre Node方式  * 时间复杂度 O(n)  * 空间复杂度 O(1)  * @param head  * @param val  * @return  */ public ListNode removeElements(ListNode head, int val) {     while(head!=null && head.val==val){//head!=null用于防止空指针错误,如果头节点就等于val的话     直接让头节点指向下一位。         head = head.next;     }     ListNode curr = head;//把头节点的位置拿到     while(curr!=null){//防止空指针         while(curr.next!=null && curr.next.val == val){//头指针的下一个也不为null,下一个为         val,那么我们的的next就要等于next.next了             curr.next = curr.next.next;         }         curr = curr.next;//把等于的值的curr返回。     }     return head; } 

1.2 虚拟头节点(!!!)

/**  * 添加虚节点方式  * 时间复杂度 O(n)  * 空间复杂度 O(1)  * @param head  * @param val  * @return  */ public ListNode removeElements(ListNode head, int val) {     if (head == null) {         return head;     }     // 因为删除可能涉及到头节点,所以设置dummy节点,统一操作     ListNode dummy = new ListNode(-1, head);     ListNode pre = dummy;     ListNode cur = head;     while (cur != null) {         if (cur.val == val) {             pre.next = cur.next;         } else {             pre = cur;         }         cur = cur.next;     }     return dummy.next; } 

2. 设计链表

//单链表 class ListNode {     int val;     ListNode next;     ListNode(){}     ListNode(int val) {         this.val=val;     } } class MyLinkedList {     //size存储链表元素的个数     int size;     //虚拟头结点     ListNode head;      //初始化链表     public MyLinkedList() {         size = 0;         head = new ListNode(0);     }      //获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点     public int get(int index) {         //如果index非法,返回-1         if (index < 0 || index >= size) {             return -1;         }         ListNode currentNode = head;         //包含一个虚拟头节点,所以查找第 index+1 个节点         for (int i = 0; i <= index; i++) {             currentNode = currentNode.next;         }         return currentNode.val;     }      //在链表最前面插入一个节点,等价于在第0个元素前添加     public void addAtHead(int val) {         addAtIndex(0, val);     }      //在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加     public void addAtTail(int val) {         addAtIndex(size, val);     }      // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。     // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点     // 如果 index 大于链表的长度,则返回空     public void addAtIndex(int index, int val) {         if (index > size) {             return;         }         if (index < 0) {             index = 0;         }         size++;         //找到要插入节点的前驱         ListNode pred = head;         for (int i = 0; i < index; i++) {             pred = pred.next;         }         ListNode toAdd = new ListNode(val);         toAdd.next = pred.next;         pred.next = toAdd;     }      //删除第index个节点     public void deleteAtIndex(int index) {         if (index < 0 || index >= size) {             return;         }         size--;         if (index == 0) {             head = head.next; 	    return;         }         ListNode pred = head;         for (int i = 0; i < index ; i++) {             pred = pred.next;         }         pred.next = pred.next.next;     } }  //双链表 class ListNode{     int val;     ListNode next,prev;     ListNode() {};     ListNode(int val){         this.val = val;     } }   class MyLinkedList {        //记录链表中元素的数量     int size;     //记录链表的虚拟头结点和尾结点     ListNode head,tail;          public MyLinkedList() {         //初始化操作         this.size = 0;         this.head = new ListNode(0);         this.tail = new ListNode(0);         //这一步非常关键,否则在加入头结点的操作中会出现null.next的错误!!!         head.next=tail;         tail.prev=head;     }          public int get(int index) {         //判断index是否有效         if(index<0 || index>=size){             return -1;         }         ListNode cur = this.head;         //判断是哪一边遍历时间更短         if(index >= size / 2){             //tail开始             cur = tail;             for(int i=0; i< size-index; i++){                 cur = cur.prev;             }         }else{             for(int i=0; i<= index; i++){                 cur = cur.next;              }         }         return cur.val;     }          public void addAtHead(int val) {         //等价于在第0个元素前添加         addAtIndex(0,val);     }          public void addAtTail(int val) {         //等价于在最后一个元素(null)前添加         addAtIndex(size,val);     }          public void addAtIndex(int index, int val) {         //index大于链表长度         if(index>size){             return;         }         //index小于0         if(index<0){             index = 0;         }         size++;         //找到前驱         ListNode pre = this.head;         for(int i=0; i<index; i++){             pre = pre.next;         }         //新建结点         ListNode newNode = new ListNode(val);         newNode.next = pre.next;         pre.next.prev = newNode;         newNode.prev = pre;         pre.next = newNode;              }          public void deleteAtIndex(int index) {         //判断索引是否有效         if(index<0 || index>=size){             return;         }         //删除操作         size--;         ListNode pre = this.head;         for(int i=0; i<index; i++){             pre = pre.next;         }         pre.next.next.prev = pre;         pre.next = pre.next.next;     } } 

206. 反转链表(双向指针和递归)

双指针

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

class Solution {     public ListNode reverseList(ListNode head) {      ListNode cur=head;      ListNode prev=null;      ListNode temp=null;       while(cur != null){         temp=cur.next;//上面这俩位置是把拿到的值拼凑到一起         cur.next=prev;         prev=cur;//下面这俩位置,是用来去拿数的         cur=temp;      }      return prev;     } } 

递归

// 递归  class Solution {     public ListNode reverseList(ListNode head) {         return reverse(null, head);     }      private ListNode reverse(ListNode prev, ListNode cur) {         if (cur == null) {             return prev;         }         ListNode temp = null;         temp = cur.next;// 先保存下一个节点         cur.next = prev;// 反转         // 更新prev、cur位置         // prev = cur;         // cur = temp;         return reverse(cur, temp);     } } 

交换链表中的元素

虚拟头节点法

class Solution {   public ListNode swapPairs(ListNode head) {         ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点         dumyhead.next = head; // 将虚拟头结点指向head,这样方便后面做删除操作         ListNode cur = dumyhead;         ListNode temp; // 临时节点,保存两个节点后面的节点         ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点         ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点         while (cur.next != null && cur.next.next != null) {             temp = cur.next.next.next;             firstnode = cur.next;             secondnode = cur.next.next;             cur.next = secondnode;       // 步骤一             secondnode.next = firstnode; // 步骤二             firstnode.next = temp;      // 步骤三             cur = firstnode; // cur移动,准备下一轮交换         }         return dumyhead.next;       } } 

递归法

// 递归版本 class Solution {     public ListNode swapPairs(ListNode head) {         // base case 退出提交         if(head == null || head.next == null) return head;         // 获取当前节点的下一个节点         ListNode next = head.next;         // 进行递归         ListNode newNode = swapPairs(next.next);         // 这里进行交换         next.next = head;         head.next = newNode;          return next;     } }  

删除链表的倒数第N个节点

class Solution {     public ListNode removeNthFromEnd(ListNode head, int n) {         //新建一个虚拟头节点指向head         ListNode dummyNode = new ListNode(0);         dummyNode.next = head;         //快慢指针指向虚拟头节点         ListNode fastIndex = dummyNode;         ListNode slowIndex = dummyNode;          // 只要快慢指针相差 n 个结点即可         for (int i = 0; i <= n; i++) {             fastIndex = fastIndex.next;         }         while (fastIndex != null) {             fastIndex = fastIndex.next;             slowIndex = slowIndex.next;         }          // 此时 slowIndex 的位置就是待删除元素的前一个位置。         // 具体情况可自己画一个链表长度为 3 的图来模拟代码来理解         // 检查 slowIndex.next 是否为 null,以避免空指针异常         if (slowIndex.next != null) {             slowIndex.next = slowIndex.next.next;         }         return dummyNode.next;     } } 

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

 ListNode slow = head;         ListNode fast = head;         while (fast != null && fast.next != null) {             slow = slow.next;             fast = fast.next.next;             if (slow == fast) {// 有环                 ListNode index1 = fast;                 ListNode index2 = head;                 // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口                 while (index1 != index2) {                     index1 = index1.next;                     index2 = index2.next;                 }                 return index1;             }         }         return null;         }         } 

链表相交(学习思路)

在这里插入图片描述

(版本一)先行移动长链表实现同步移动 public class Solution {     public ListNode getIntersectionNode(ListNode headA, ListNode headB) {         ListNode curA = headA;         ListNode curB = headB;         int lenA = 0, lenB = 0;         while (curA != null) { // 求链表A的长度             lenA++;             curA = curA.next;         }         while (curB != null) { // 求链表B的长度             lenB++;             curB = curB.next;         }         curA = headA;         curB = headB;         // 让curA为最长链表的头,lenA为其长度         if (lenB > lenA) {             //1. swap (lenA, lenB);             int tmpLen = lenA;             lenA = lenB;             lenB = tmpLen;             //2. swap (curA, curB);             ListNode tmpNode = curA;             curA = curB;             curB = tmpNode;         }         // 求长度差         int gap = lenA - lenB;         // 让curA和curB在同一起点上(末尾位置对齐)         while (gap-- > 0) {             curA = curA.next;         }         // 遍历curA 和 curB,遇到相同则直接返回         while (curA != null) {             if (curA == curB) {                 return curA;             }             curA = curA.next;             curB = curB.next;         }         return null;     }  }  (版本二) 合并链表实现同步移动 public class Solution {     public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 		// p1 指向 A 链表头结点,p2 指向 B 链表头结点 		ListNode p1 = headA, p2 = headB; 		while (p1 != p2) { 			// p1 走一步,如果走到 A 链表末尾,转到 B 链表 			if (p1 == null) p1 = headB; 			else            p1 = p1.next; 			// p2 走一步,如果走到 B 链表末尾,转到 A 链表 			if (p2 == null) p2 = headA; 			else            p2 = p2.next; 		} 		return p1;     } } 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!