内容简介:输入一个链表的头节点,从尾到头反过来打印出每个节点的值例如链表为1->2->3,打印出3 2 1有三种思路可以参考:
输入一个链表的头节点,从尾到头反过来打印出每个节点的值
例如链表为1->2->3,打印出3 2 1
有三种思路可以参考:
- 栈:栈天然是先进后出的,在遍历链表时,把值按顺序放入栈中,最后出栈就是逆序了。
- 既然想到了栈,那么递归天然是一个栈结构,一样的,访问到一个节点,先递归输出它的后面节点,再输出自己,这样链表就反过来了
- 头插法:使用头插法可以得到一个逆序的链表。
- 1->2->3,加入一个头节点head,只用于操作中转,不存储数据
- 先把head->1,然后继续从2->3中取
- 变为head->2->1,最后变为head->3->2->1,从head.next开始输出就可以了
public class _06 {
static class ListNode {
int value;
public ListNode(int value) {
this.value = value;
}
ListNode next;
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
listNode1.next = listNode2;
listNode2.next = listNode3;
_06 test = new _06();
// List<Integer> list = test.printListFromQueue(listNode1);
// List<Integer> list = test.printListFromDG(listNode1);
List<Integer> list = test.printListFromTC(listNode1);
for (Integer i : list) {
System.out.println(i);
}
}
/**
* 栈
*/
public List<Integer> printListFromQueue(ListNode listNode) {
Stack<Integer> stack = new Stack<>();
while (listNode != null) {
stack.add(listNode.value);
listNode = listNode.next;
}
List<Integer> list = new ArrayList<>();
while (!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
/**
* 递归
*/
public List<Integer> printListFromDG(ListNode listNode) {
List<Integer> list = new ArrayList<>();
if (listNode != null) {
list.addAll(printListFromDG(listNode.next));
list.add(listNode.value);
}
return list;
}
/**
* 头插法
*/
public List<Integer> printListFromTC(ListNode listNode) {
//头插法构建逆序链表
//新建一个头部节点,只用于中转,不存储值
ListNode head = new ListNode(-1);
//链表反转
while (listNode != null) {
ListNode temp = listNode.next;
listNode.next = head.next;
head.next = listNode;
listNode = temp;
}
List<Integer> list = new ArrayList<>();
//真正的值是从第一个节点开始
head = head.next;
while (head != null) {
list.add(head.value);
head = head.next;
}
return list;
}
}
复制代码
删除链表的节点
在O(1)的时间内删除链表节点
给定单身链表的头指针和一个节点指针,定义一个函数在O(1)内删除这个节点。
解题思路
-
如果这个节点不是尾节点,那么可以直接把下一个节点的值赋值给这个节点,再把这个节点指向下下个节点,时间复杂度为O(1)
如,1->2->3->4,要删除的是2,那么先把2的下一个节点值3,赋值给2的节点,变为1-3-3-4,然后把第二个节点的位置,指向下下个节点4,这样,就变为了1-3-4,2这个节点这删除了。
-
如果是尾指针,那么找到前一个节点,把前一个节点指向null,时间复杂度为O(N)
如果进行N次操作,这个算法的平均时间复杂度为O(1)
/**
* 删除指定节点
*/
public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
if (head == null || tobeDelete == null) {
return null;
}
if (tobeDelete.next != null) {
//要删除的不是尾节点
ListNode next = tobeDelete.next;
tobeDelete.value = next.value;
tobeDelete.next = next.next;
} else {
if (head == tobeDelete) {
head = null;
} else {
ListNode curNode = head;
while (curNode.next != tobeDelete) {
curNode = curNode.next;
}
curNode.next = null;
}
}
return head;
}
复制代码
删除链表中重复的节点
在一 排序 的链表中,如何删除重复的节点
1->2->2->3->3->3->4
1->4
解题思路
- 头节点一样有可能重复
- 遍历整个链表,如果当前节点与next的下一个节点相同,则都可以删除,然后把当前节点的前一个节点与下下个节点相连,我们必须保证前一个节点是无一个无重复的节点相连,这一想就是递归~~
/**
* 删除重复的节点
*/
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null || pHead.next == null) {
return pHead;
}
ListNode next = pHead.next;
//当头节点有重复时,一直找到没有重复的为止
if (pHead.value == next.value) {
while (next != null && pHead.value == next.value) {
next = next.next;
}
return deleteDuplication(next);
} else {
pHead.next = deleteDuplication(pHead.next);
return pHead;
}
}
复制代码
链表中倒数第K个节点
输入一个单向链表,输出这个链表中倒数第k个节点
1->2->3->4->5->6
倒数第三个点为4
解题思路
这是一个单向链表,如果我们使用遍历,很明显不方便从尾开始遍历的,这时候我们可以想到的方法是双指针法。
- 定义两个指针p1和p2,p2先不动,p1走k-1步,这时p1指向3,p2指向1
- 现在p1和p2同时向后遍历,当p1遍历到6,它的next为null时,p2所指向的值就是我们所要的值
public ListNode FindKthToTail(ListNode head, int k) {
if (head == null) {
return null;
}
ListNode p1 = head;
while (p1 != null && k > 0) {
p1 = p1.next;
k--;
}
if (k > 0) {
return null;
}
ListNode p2 = head;
while (p1 != null) {
p1 = p1.next;
p2 = p2.next;
}
return p2;
}
复制代码
反转链表
解题思路
-
头插法(迭代)
- 前面其实已经用到了,使用一个头head,只做中转,而不存储数据。
-
递归
分解成最简单的两个节点,就next.next = head,这样就能把1->2反转成2->1。1的next是2,它的next指向1,就已经完成了2->1
public ListNode reverseList(ListNode head){
if (head==null||head.next==null){
return head;
}
ListNode next = head.next;
head.next = null;
ListNode newHead = reverseList(next);
next.next = head;
return newHead;
}
public ListNode reverseList2(ListNode head){
if (head==null||head.next==null){
return head;
}
ListNode newList = new ListNode(-1);
while (head!=null){
ListNode next = head.next;
head.next = newList.next;
newList.next = head;
head = next;
}
return newList.next;
}
复制代码
合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新的链表中的节点仍然是递增的。
1-3-5-7
2-4-6-8
1-2-3-4-5-6-7-8
解题思路
首先我们要比较两个链表头,发现1比2小,那么新链表的头就是1,然后继续比较,这时还是比较链表头,我们想到了,这就是个递归啊,把头继续比较,依次加进新的链表中,所以本题可以用递归来解,当然也能使用迭代。
/**
* 递归
*/
public ListNode merge(ListNode node1, ListNode node2) {
if (node1 == null) {
return node2;
}
if (node2 == null) {
return node1;
}
if (node1.value < node2.value) {
node1.next = merge(node1.next, node2);
return node1;
} else {
node2.next = merge(node1, node2.next);
return node2;
}
}
/**
* 迭代
*/
public ListNode merge2(ListNode node1, ListNode node2) {
ListNode head = new ListNode(-1);
ListNode cur = head;
while (node1 != null && node2 != null) {
if (node1.value <= node2.value) {
cur.next = node1;
node1 = node1.next;
} else {
cur.next = node2;
node2 = node2.next;
}
cur = cur.next;
}
if (node1 != null) {
cur.next = node1;
}
if (node2 != null) {
cur.next = node2;
}
return head.next;
}
复制代码
两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点
1-2-3-6-7
4-5-6-7
解题思路
-
暴力破解法
在第一个链表上顺序遍历到一个节点,就到第二个链表上遍历每一个节点,如果有一个节点与这个值相等,就找到了。但是时间复杂度为O(mn)
-
栈
我们发现,公共部分一定是在尾部,如果我们从后面开始找会更快,最后一个相同点就是我们要找的点,不过这是单向链表,所以我们想到了栈的结构——后进先出
-
求差法
其实我们用栈,只是为了想同时能到达两个链表的尾节点。当两链表长度不同时,我们从头开始遍历,到后面的时间就不一致。 我们可以先遍历两个链表,得到它们的长度,然后,链表长的先走先走多的几个节点,接着同时在两个链表遍历,找到的第一个相同点就是我们要找的第一个公共点
/**
* 用栈取
*/
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Stack<ListNode> stack1 = new Stack<>();
Stack<ListNode> stack2 = new Stack<>();
//分别放入栈中
while (pHead1 != null) {
stack1.push(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null) {
stack2.push(pHead2);
pHead2 = pHead2.next;
}
ListNode result = null;
//从栈顶取值,取到不相等时,上一个就是第一个公共节点
while (!stack1.isEmpty() && !stack2.isEmpty() && stack1.peek() == stack2.peek()) {
stack1.pop();
result = stack2.pop();
}
return result;
}
/**
* 差值法
*/
public ListNode findFirstCommonNode2(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
int count1 = 1;
int count2 = 1;
ListNode p1 = pHead1;
ListNode p2 = pHead2;
//获取两个链表的长度
while (p1.next != null) {
p1 = p1.next;
count1++;
}
while (p2.next != null) {
p2 = p2.next;
count2++;
}
//哪个长就先走差值步
if (count1 > count2) {
int dif = count1 - count2;
while (dif != 0) {
pHead1 = pHead1.next;
dif--;
}
} else {
int dif = count2 - count1;
while (dif != 0) {
pHead2 = pHead2.next;
dif--;
}
}
//同时走,当相等时得到公共节点
while (pHead1 != null && pHead2 != null) {
if (pHead1 == pHead2) {
return pHead1;
}
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
return null;
}
复制代码
By《剑指offer》
下面是我的公众号,欢迎大家关注我
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Filter Bubble
Eli Pariser / Penguin Press / 2011-5-12 / GBP 16.45
In December 2009, Google began customizing its search results for each user. Instead of giving you the most broadly popular result, Google now tries to predict what you are most likely to click on. Ac......一起来看看 《The Filter Bubble》 这本书的介绍吧!