- 复习了一下链表,以前用C实现过,现在用Java实现了一下,实现了一些最基本的操作,并测试了一下。之后学到LinkedList类的时候再比较一下。
- C/C++和Java还是有很多不同的,Java中全部都成了类,没有了结构体,也没有了头文件,也不需要指针,链表实现起来比C方便些。以后要总结一下两个高级语言的一些区别。
package LinkList;
public class LinkListDemo {
public static void main(String[] args) {
// 创建链表和结点
LinkList l = new LinkList();
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
// 插入结点
l.addNode(node1);
l.addNode(node2);
l.addNode(node3);
// 遍历输出
l.outputList();
}
}
// 单链表结点的定义
class ListNode {
int val; // 存放节点数据域
ListNode next = null; // 指向下一个节点
// 构造方法
ListNode(int val) {
this.val = val;
}
}
// 单链表的相关操作集
class LinkList {
// 初始化链表
private ListNode head = new ListNode(0); // 初始化头结点
// 尾插法添加结点
public void addNode(ListNode listNode) {
ListNode temp = head; // 辅助遍历链表
while(true) {
// 找到尾结点
if(temp.next == null) {
break;
}
// 继续查找
temp = temp.next;
}
// 最后结点指向新的结点
temp.next = listNode;
}
// 输出单链表数据
public void outputList() {
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
ListNode temp = head.next; // 辅助遍历链表
while(true) {
if(temp == null) {
break;
}
// 输出数据,指针后移
System.out.println(temp.val);
temp = temp.next;
}
}
}
题目:输入一个链表的头节点,从尾到头反过来打印出每个节点的值。'
链表节点与题目定义如下:
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
}
}
- 算法分析:如何实现从头到尾输出?
- 可改变结构:让链表的指针反转即可。
- 不改变结构:所以可以用栈进行递归。
- 程序分析:牛客网给到的结点与函数定义中
- 输出值类型为ArrayList,输入值为链表结点
- 不可以直接使用LinkedList类,可以使用ArrayList类
- 解决问题:最简单的还是调用API,ArrayList.add(index,value)方法就很方便
- 多种解法:不改变结构的话,栈可以用系统栈和API中的Stack类
- 方法分析:三种方法的复杂度差不多,都是O(n)。但是算法练习中少用API的好,所以用系统栈来进行递归是最应景的,但缺点是链表太长可能会使栈溢出。
- 一些细节:输入结点应为链表的首元结点,不需要头结点。(程序中不需要插入、删除操作)
package LinkList;
/*题目:输入一个链表的头节点,从尾到头反过来打印出每个节点的值。'
链表节点定义如下:
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
import java.util.ArrayList;
import java.util.Stack;
public class PrintListFromTailToHead {
public static void main(String[] args){
// 初始化链表和结点
LinkList l = new LinkList();
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
// 插入结点
l.addNode(node1);
l.addNode(node2);
l.addNode(node3);
// 遍历输出
// l.outputList();
ArrayList<Integer> list = printListFromTailToHead1(node1); // 输入首元结点,创建逆序链表
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
// 非递归:利用 ArrayList.add(index,value),可以指定 index 位置插入 value 值
// 遍历链表的同时将每个遇到的值插入到 ArrayList 的 0 位置(头插法),最后输出即可得到逆序链表。
public static ArrayList<Integer> printListFromTailToHead1(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
// 辅助元素遍历链表
ListNode tmp = listNode;
// 结点不为空,则插入 list 头部,并移动指针
while(tmp !=null){
list.add(0,tmp.val);
tmp = tmp.next;
}
return list;
}
// 因为是从头到尾输出,也就是先进后出,要用递归,也就是用栈来实现。
// 缺点是链表太长可以会使栈溢出。
// 递归方法:使用Stack类
public static ArrayList<Integer> printListFromTailToHead2(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
Stack<Integer> stack = new Stack<>();
while (listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
while (!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
// 递归方法:使用系统栈
public ArrayList printListFromTailToHead3(ListNode listNode) {
ArrayList list = new ArrayList();
if(listNode!=null){
printListFromTailToHead3(listNode.next);
list.add(listNode.val);
}
return list;
}
}