这几天老是看到链表的题目,不是链表的反转,就是链表求和……必须把它攻克了,不然题目都刷不了了,所以这两天一直在找相关资料,所以没有刷很太多题。
为了靠自己不看题解地去实现链表的反转打印这题以及后期的链表题目。我不断反思自己为什么对链表有点害怕,甚至不敢开始学习链表的知识,最后我总结如下:
-
大一的时候学C语言的时候感觉链表怎么这么长,被长度给下住了。代码一长也就意味着里面会有些复杂性,而且是和指针有关,是不好理解。
-
现在接触了C++,java,可以不需要太多地接触指针了,但是对一些函数的用法很生疏,需要了解和自己写代码来帮助熟悉。
-
其实对于链表,我看了很多基础知识,对它的结构是大致有些了解的,但是每到实现的时候,就先想到看别人的代码了,感觉自己不能理解,先找大佬的代码入入门,但这样一来,对我来说反而是有害的。这有一种先入为主,如果没有理解清楚的话就是靠记忆去实现代码了。
-
对我来说,代码实现是最难的,虽然可以理解个大概,但是实际操作起来就时时出问题。
-
但是今天我给自己提了一个方法:反问自己,针对问题,逐个击破。
第一:链表的基本结构是怎么样的你知道了吗?
第二:链表又可以分为几种?
第三:链表的结构特点和数组有什么区别?
第四:根据它的结构,尝试着写写遍历的过程?
第五:再想想它是怎么实现插入和删除的?
第二个问题:链表有单链表、双链表、循环列表
第三个问题:链表的链式结构在物理存储上是不一定连续的,它可以实现充分利用内存的效果,属于动态分配内存;而数组就必须是固定长度的连续内存,属于静态内存分配。
第四个问题:
链表的普通赋值与打印——从最简单的开始
package ListNode;
/**第一节:
* 单链表
* 普通创建和打印
* @author
*
*/
public class ListNodeCommonTraverse {
public static void main(String[] args) {
ListNode node1 = new ListNode(10);
ListNode node2 = new ListNode(11);
ListNode node3 = new ListNode(12);
ListNode node4 = new ListNode(13);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = null;
ListNode p = new ListNode();
p = node1;
while(p!=null) {
System.out.print(p.value+" ");
p = p.next; //往下面走,继续打印
}
}
}
class ListNode{
public int value;
public ListNode next;
ListNode(){}
ListNode(int x){
this.value = x;
}
}
//10 11 12 13
链表的循环创建与遍历
package ListNode;
/**第二节
* 单链表
* 循环创建和遍历
*/
public class ListNodeCircleTraverse {
public static void main(String[] args) {
ListNode head = new ListNode();//头指针
ListNode p = new ListNode(); //遍历指针
head.value = 100;
head.next = null;
p = head;
for(int i=0; i<10; i++) {
ListNode q = new ListNode(); //新节点创建
q.value = i;
p.next = q;
p = q;
}
p.next = null;
//遍历
System.out.println("普通遍历");
while(head!=null) {
System.out.print(head.value+" ");
head = head.next;
}
}
}
普通遍历
100 0 1 2 3 4 5 6 7 8 9
链表的遍历+栈+队列
package ListNode;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**第三节
* ListNode的遍历(traverse)
*
*/
public class ListNodeTraverse {
public static void main(String[] args) {
ListNode head = new ListNode();//头指针
ListNode p = new ListNode(); //遍历指针
head.value = 100;
head.next = null;
p = head;
for(int i=0; i<10; i++) {
ListNode q = new ListNode(); //新节点创建
q.value = i;
p.next = q;
p = q;
}
p.next = null;
Queue<Integer> queue = new LinkedList<Integer>();
Stack<Integer> stack = new Stack<Integer>();
while(head!=null) {
queue.add(head.value);
stack.add(head.value);
head = head.next;
}
System.out.println("队列出队");
while(!queue.isEmpty()) {
System.out.print(queue.poll()+" ");
}
System.out.println();
System.out.println("栈出栈");
while(!stack.isEmpty()) {
System.out.print(stack.pop()+" ");
}
}
}
队列出队
100 0 1 2 3 4 5 6 7 8 9
栈出栈
9 8 7 6 5 4 3 2 1 0 100
牛客的剑指offer第3题-链表反转题解:
最后通过了链表反转这题:
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> ArrayList1 = new ArrayList<Integer>();
ListNode list;
list = listNode;
Stack<Integer> sta = new Stack<Integer>();
while(listNode!=null){
sta.add(listNode.val);
listNode = listNode.next;
}
while(!sta.isEmpty()){
ArrayList1.add(sta.pop());
}
return ArrayList1;
}
}
链表的插入与删除
package ListNode;
/**第四节
* 链表的插入与删除
*/
public class ListNodeInsert_Delete {
public static void main(String[] args) {
ListNode head = new ListNode(); //头指针对象
ListNode p = new ListNode(); //遍历指针对象
ListNode qqHead = new ListNode();//遍历辅助指针对象
head.value = 100;
head.next = null;
p = head;
//初始链表数据输入
for(int i=0; i<10; i++) {
ListNode q = new ListNode(); //新节点创建
q.value = i;
p.next = q;
p = q;
}
p.next = null;
System.out.println("链表的原始数据");
qqHead = head;
while(qqHead!=null) {
System.out.print(qqHead.value+" ");
qqHead = qqHead.next;
}
System.out.println();
/**链表插入
* 下面实现的是链表插入结尾的程序
* 变式:可以写在指定位置插入数据,如遍历到指定的数据或者个数时插入
* 实现方式:需要三个指针,前before,当前指针curr,后next
*/
//新链表创建
ListNode qq = new ListNode(); //新节点创建
ListNode qhead = new ListNode();
qq.value = 100;
qq.next = null;
//新链表数据插入
p.next=qq;
qqHead = head; //遍历结点重新指到头结点
System.out.println("链表的插入操作");
while(qqHead!=null) {
System.out.print(qqHead.value+" ");
qqHead=qqHead.next;
}
System.out.println();
/**链表数据删除
* before
* curr
* next
*/
System.out.println("链表的删除操作");
ListNode before = new ListNode();
ListNode curr = new ListNode();
ListNode next = new ListNode();
before = null;
curr = head;
next = null;
while(curr!=null) {
next = curr.next; //确定当前指针的下个指针
if(curr.value == 3) {
before.next = next;//后面结点地址给到前面结点,跳过当前结点
}
before = curr; //更新before指针
curr = curr.next; //更新单前指针
}
p = head;
while(p!=null) {
System.out.print(p.value+" ");
p = p.next;
}
}
}
链表判断是否成环
package ListNode;
/**第五节
* 链表是否有环?
* 快慢指针遍历
* 慢指针一次走1个,快指针一次走2个,如果成环,定能碰到,否则出界
*/
public class ListNodeHasCircle {
/**
* 判断是否是环状结构
* @param head
*/
public static void ListNodeHasCircle(ListNode head) {
ListNode slow = new ListNode();
ListNode fast = new ListNode();
System.out.println("是否为环状?");
slow = head;
fast = head;
while(slow!=null && fast.next!=null) {
slow = slow.next;
fast = fast.next.next;
if(slow.equals(fast)) {
System.out.println("Yes,it is a circle list!");
return;
}
}
System.out.println("Sorry,it is not a circle list!");
}
public static void main(String[] args) {
ListNode head = new ListNode(); //头指针对象
ListNode p = new ListNode(); //遍历指针对象
head.value = 100;
head.next = null;
p = head;
for(int i=0; i<10; i++) {
ListNode q = new ListNode(); //新节点创建
q.value = i;
p.next = q;
p = q;
}
p.next = head;//构建循环链表
ListNodeHasCircle(head);
}
}
上面的5个程序只是我的测试代码,还未做过整理,接下来做一个优化整理,将这些方法整理成类中的方法,如下,但是有欠缺,比如异常抛出没有定义相关的类来处理
ListNode类
package com.ListNode;
/**
* 19.9.17
*
*/
public class ListNode {
public int value;
public ListNode next;
ListNode(){}
//链表创建
public void add(ListNode head, int x){
head.value = x;
// head = head.next;
// System.out.println(head);
}
/**链表某个索引处插入某个链表
* IndexInsert
* @param head
* @param index
* @return
*/
public ListNode IndexInsert(ListNode head,int index,ListNode another) {
if(another == null || head == null) {
//抛异常
System.out.println("List is null!");
return null;
}
int i=0;
ListNode cur = new ListNode();
ListNode now = new ListNode();
cur = head;
while(cur.next!=null) {//注意点,是newOne的下个结点为空时退出
if(i==index) {
now = cur.next; //记录中断点后面的地址
while(another!=null) {
cur.next = another;
if(another.next!=null)
another = another.next;
else {
cur = cur.next;
cur.next = now;
break;
}
}
// break;//连接结束
}
i++;
cur = cur.next;
}
if(index > i) {//索引超出
//抛异常
System.out.println("Sorry,index is out of size!");
}
return head;
}
/**索引处删除
* IndexDelete
* @param head
* @param index
* @return
*/
public ListNode IndexDelete(ListNode head, int index) {
if(head == null)
return null;
int i=0;
ListNode pre = null;
ListNode cur = head;
ListNode next = null;
while(cur!=null) {
next = cur.next; //更新后指针
if(index == i) { //找到索引处
pre.next = next;//后面结点地址给到前面结点,跳过当前结点
}
i++;
pre = cur; //更新pre指针
cur = cur.next; //更新当前指针
}
return head;
}
/**判断是否成环
* isCircleList
* @param head
* @return
*/
public boolean isCircleList(ListNode head) {
if(head == null)
return (Boolean) null;
ListNode slow = new ListNode();
ListNode fast = new ListNode();
System.out.println("是否为环状?");
slow = head;
fast = head;
try {
while(slow!=null && fast.next!=null) {
slow = slow.next;
fast = fast.next.next;
if(slow.equals(fast)) {
// System.out.println("Yes,it is a circle list!");
return true;
}
}
// System.out.println("Sorry,it is not a circle list!");
}catch (Exception e) {}
return false;
}
public void ListDisplay(ListNode head) {
if(head == null)
return;
ListNode node = new ListNode();
node = head;
while(node!=null) {
System.out.print(node.value+" ");
node = node.next;
}
}
}
主函数:
package com.ListNode;
/**
* 19.9.17
*
*/
public class TestList {
public static void main(String[] args) {
ListNode Node1 = new ListNode();
ListNode Node2 = new ListNode();
ListNode p = new ListNode();
Node1.value = 100;
Node1.next = null;
Node2.value = 999;
Node2.next = null;
p = Node1;
for(int i=0;i<10;i++) {
ListNode q = new ListNode();
q.add(q, i);
p.next = q;
p = q;
}
System.out.println("打印链表");
Node1.ListDisplay(Node1);
System.out.println();
System.out.println("链表插入");
Node1.IndexInsert(Node1, 4, Node2);
Node1.ListDisplay(Node1);
System.out.println();
System.out.println("删除链表指定元素");
Node1.ListDisplay(Node1.IndexDelete(Node1, 5));
System.out.println();
System.out.println("判断链表是否成环");
System.out.println(Node1.isCircleList(Node1));
}
}
结果打印
1.打印链表
100 0 1 2 3 4 5 6 7 8 9
2.链表插入
100 0 1 2 3 999 4 5 6 7 8 9
3.删除链表指定元素
100 0 1 2 3 4 5 6 7 8 9
4.判断链表是否成环
是否为环状?
false