моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
💎 欢迎各位大佬互三:моя домашняя страница
Подумайте: если вы не открываете дополнительное пространство, а изменяете только исходный связанный список, какой метод следует использовать?
Просто начните со второго элемента и последовательно выполняйте вставку головки.
Затем просто измените ссылку и укажите последнему элементу значение null.
- class Solution {
- public ListNode reverseList(ListNode head) {
- //head为null,直接返回
- if(head == null){
- return head;
- }
-
- ListNode cur = head.next;
- //head变为了末尾元素,指向null
- head.next = null;
- while(cur!=null){
- //curn为cur的next节点,便于往后移动
- ListNode curn = cur.next;
- cur.next = head;
- head = cur;
- //更新cur
- cur = curn;
- }
- return head;
- }
- }
876. Промежуточный узел связного списка.
Первый метод, который приходит на ум при ответе на этот вопрос, — это определить cnt, один раз пройти по связанному списку, затем найти среднее число и затем пройти по возвращаемому значению. Этот метод очень прост, но что, если вам нужно пройти по всему списку? связанный список только один раз, чтобы найти промежуточный узел?
Вот новая идея: используйте быстрый и медленный указатели, начиная с головы. Быстрый указатель каждый раз перемещает два узла, а медленный — один узел. Достигается ли это конечной цели?
Тогда давайте реализуем это:
- class Solution {
- public ListNode middleNode(ListNode head) {
- if(head == null){
- return null;
- }
- ListNode fast = head;
- ListNode slow = head;
- while(fast!= null&&fast.next!= null){
- fast = fast.next.next;
- slow = slow.next;
- }
- return slow;
- }
- }
Будет ли это более эффективно?
Вопрос на собеседовании 02.02. Вернуть k-й узел из последнего.
Первое, что приходит на ум по этому вопросу, — пройти его один раз, чтобы получить общую длину, а затем найти k-й узел из последнего. Однако на основе предыдущего вопроса можно придумать другой метод: Сходным образом,Используя указатели «быстрый» и «медленный», сначала пропустите k-1 узлов быстро, а затем одновременно пройдите медленный и быстрый. Когда следующий из «быстрых» равен нулю, это означает, что «быстрый» достиг последнего узла, а «медленный» в это время. — k-й узел снизу.
Этот вопрос указывает на то, что ввод k является допустимым, поэтому не требуется никаких дополнительных суждений. Как определить, является ли k незаконным, и требуется, чтобы длина связанного списка не могла быть найдена напрямую, поэтому медленно нужно оценивать во время ходьбы.
- class Solution {
- public int kthToLast(ListNode head, int k) {
- if (head == null) {
- return -1;
- }
- ListNode fast = head;
- ListNode slow = head;
- int cnt = 0;
- while (cnt != k - 1) {
- //判断k的合法性,虽然本题不用判断
- fast = fast.next;
- if(fast == null){
- return;
- }
- cnt++;
- }
- //slow和fast同时移动
-
- while (fast.next != null) {
- fast = fast.next;
- slow = slow.next;
- }
- return slow.val;
- }
- }
21. Объединить два упорядоченных связанных списка.
Идея:Вам нужно всего лишь определить новый узел newH, а затем сравнить значения headA и headB. Чтобы наконец найти головной узел объединенного связанного списка, вам также необходимо определить tmp = newH, а затем тот, у которого меньшее значение. tmp.next укажет на это.
- class Solution {
- public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
- ListNode head = new ListNode();
- ListNode tmp = head;
-
- while(list1!=null&&list2!=null){
- if(list1.val < list2.val){
- tmp.next = list1;
- list1 = list1.next;
- tmp = tmp.next;
- }else{
- tmp.next = list2;
- list2 = list2.next;
- tmp = tmp.next;
- }
- }
- //判断剩余的情况
- if(list1!=null){
- tmp.next = list1;
- }else if(list2!=null){
- tmp.next = list2;
- }
- return head.next;
- }
- }
Разделение связанного списка CM11
Это вопрос для собеседования на Niuke. Смысл вопроса состоит в том, чтобы разделить связанный список. Левая часть x меньше x, а правая часть больше x. Исходный порядок изменить нельзя.
Идея: определить новый узел Cur для обхода исходного связанного списка (чтобы предотвратить изменение исходного связанного списка), поместить все, что больше x, в связанный список, поместить все, что меньше x, в связанный список и, наконец, соединить их. связанные списки.
- public class Partition {
- public ListNode partition(ListNode pHead, int x) {
- ListNode bs = null;
- ListNode be = null;
- ListNode as = null;
- ListNode ae = null;
- ListNode cur = pHead;
- while (cur != null) {
- if (cur.val < x) {
- //处理刚开始为null的情况
- if (bs == null) {
- bs = be = cur;
- }else{
- be.next = cur;
- be = be.next;
- }
- }else{
- if(as == null){
- as = ae = cur;
- }else{
- ae.next = cur;
- ae = ae.next;
- }
- }
- cur = cur.next;
- }
- //都是比x大的情况
- if(bs == null){
- return as;
- }
- be.next = as;
- //最后都要置为null,不然可能会报错
- if(as!=null){
- ae.next = null;
- }
- return bs;
- }
- }
Следует отметить, что если они больше x, то есть bs = null, то as будет возвращено напрямую, а если as.next окончательно будет установлен в значение null
Структура палиндрома OR36 связанного списка
Это вопрос от Niuke, потому что у него есть требования к временной и пространственной сложности, а это значит, что вы не можете открыть дополнительный массив, иначе пространственная сложность не будет преодолена, и нормальный обход будет иметь сложность O(n)
Идея такая: сначала найти средний узел медленного связанного списка, а затем перевернуть часть от середины к концу. Головной узел проходится назад, а медленный узел также проходится назад. значения разные, значит не возвращают текстовую структуру.
Обращение связанного списка и поиск промежуточного узла практиковались и раньше.
- public class PalindromeList {
- public boolean chkPalindrome(ListNode head) {
- if (head == null) {
- return true;
- }
- //找到中间节点
- ListNode fast = head;
- ListNode slow = head;
- while (fast != null && fast.next != null) {
- fast = fast.next.next;
- slow = slow.next;
- }
- //从slow开始到末尾反转
- ListNode cur = slow.next;
- while (cur != null) {
- ListNode curn = cur.next;
- cur.next = slow;
- slow = cur;
- cur = curn;
- }
- //判断回文
- while (head != slow) {
- if (head.val != slow.val) {
- return false;
- }
- //判断偶数节点
- if (head.next == slow) {
- return true;
- }
- head = head.next;
- slow = slow.next;
- }
- return true;
- }
- }
Следует отметить, что существует также разница в оценке связанного списка узлов с нечетными номерами и связанного списка узлов с четными номерами.
Если узел с четным номером переместить наверх, это вызовет бесконечный цикл, поэтому для узла с четным номером потребуется дополнительная оценка.
160. Пересекающиеся связанные списки
Пересекающийся связанный список — это преобразование, аналогичное «Y», как показано на рисунке.
Картина в вопросе ниже также очень ясна.
Идея: потому что независимо от того, является ли A коротким или B, разница между ними - это часть до пересечения. Вам нужно всего лишь переместить длинный связанный список на разницу, а затем два узла связанного списка одновременно перемещаются назад. одинаковые узлы должны пересекать узел.
- public class Solution {
- public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
- ListNode pl = headA;
- ListNode ps = headB;
- int cnta = 0,cntb = 0;
- //计算两个链表的长度
- while(pl!=null){
- pl = pl.next;
- cnta++;
- }
- while(ps!=null){
- ps = ps.next;
- cntb++;
- }
- //如果链表b长,就更新pl,ps
- int len = cnta - cntb;
- //上面求长度时pl,ps都为空了,所以这里再重新指向
- pl = headA;
- ps = headB;
- if(len < 0){
- pl = headB;
- ps = headA;
- len = cntb - cnta;
- }
- //移动差值
- while(len!=0){
- pl = pl.next;
- len--;
- }
- //找到相交节点
- while(pl!=ps){
- pl = pl.next;
- ps = ps.next;
- }
- //链表不相交的情况
- if(pl == null) return null;
- return pl;
- }
- }
Наконец, нам нужно добавить непересекающуюся ситуацию. Хотя мы можем пропустить этот вопрос, не добавляя его, нам все равно придется добавить его для строгости.
141. Круговой связанный список
Как определить, есть ли в связанном списке цикл:По-прежнему используйте указатель скорости, так же, как два человека бегут по детской площадке: один бежит быстро, а другой бежит медленно. Поскольку игровая площадка представляет собой кольцо, быстрый человек определенно может превзойти медленного. В этот раз они встретились. , но если бы не Тамаки, они бы никогда не смогли встретиться, как богиня, которую не поймаешь, как бы сильно ни гонялся.
Но есть и другая ситуация. Если это кольцо с двумя узлами, и определенный быстрый указатель делает 3 шага за раз, а медленный указатель делает 1 шаг за раз, то они никогда не встретятся.
Поэтому определившимся указателям необходимо каждый раз судить, сколько шагов они сделают. Если один сделает 2 шага, а другой — 1 шаг, они обязательно встретятся.
- public class Solution {
- public boolean hasCycle(ListNode head) {
- ListNode fast = head;
- ListNode slow = head;
- while(fast!=null&&fast.next!=null){
- fast = fast.next.next;
- slow = slow.next;
- if(fast == slow) return true;
- }
- return false;
- }
- }
142. Циркулярный связанный список II.
На этот раз вам нужно узнать точку входа в кольцо, рассудив, является ли это кольцом в предыдущем вопросе. Вы можете сделать следующие рассуждения.
- public class Solution {
- public ListNode detectCycle(ListNode head) {
- ListNode fast = head;
- ListNode slow = head;
- //判断环
- while(fast!=null&&fast.next!=null){
- fast = fast.next.next;
- slow = slow.next;
- if(fast == slow) break;
- }
- if(fast == null||fast.next == null) return null;
- slow = head;
- //寻找环
- while(slow!=fast){
- slow = slow.next;
- fast = fast.next;
- }
- return slow;
- }
- }
Что необходимо добавить, так это:
Когда кольцо очень маленькое, окончательный результат упрощения будет таким, как указано выше, но исходный код по-прежнему в порядке.