目录
链表(Linked List)介绍【单链表篇】
单链表介绍
单链表模拟
1. 定义pojo
2. 定义内部类SingleLinkedList 管理我们的pojo对象,并实现增删改查方法(我们这实现按数字编号自然排序的单链表)
面试题目1:获取倒数第N个节点
面试题目2:获取倒数第N个节点
面试题3:逆序打印(这里使用栈的方式)
链表包括:1.单链链表 ; 2.双链链表 ; 3. 环状链表
链表是有序的列表,但是它在内存中是存储如下
1)链表是以节点的方式来存储,是链式存储;
2)每个节点包含 data 域, next 域:指向下一个节点;
3)如图:发现链表的各个节点不一定是连续存储;
4) 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定。
单链表(带头结点) 逻辑结构示意图如下:
单链表:在结点中数据域【既图中的头、a1....aa】用来存储数据元素,指针域用于指向下一个具有相同结构的结点。因为只有一个指针结点,称为单链表。
单链表三个概念需要区分清楚:分别是头指针,头节点和首元节点。
若头结点的指针域为空(NULL),表明链表是空表。头结点对于链表来说,不是必须的,在处理某些问题时,给链表添加头结点会使问题变得简单。
首元结点:链表中第一个元素所在的结点,它是头结点后边的第一个结点。
头指针:永远指向链表中第一个结点的位置(如果链表有头结点,头指针指向头结点;否则,头指针指向首元结点)。
头结点和头指针的区别: 1)头指针是一个指针,头指针指向链表的头结点或者首元结点;2)头结点是一个实际存在的结点,它包含有数据域和指针域;3)两者在程序中的直接体现就是:头指针只声明而没有分配存储空间,头结点进行了声明并分配了一个结点的实际物理内存。
单链表中可以没有头结点,但是不能没有头指针!
头节点的引入能使链表对第一个元素的删除和插入和其他元素相同,不用另外说明,使得代码更加简洁。
单链表的基本操作:
单链表的基本操作有:增(add),删(remove),改(set),查(find),插(insert)等。
单链表模拟
我们来依次实现单链表方法:
首先,我们制定一个数据的相关信息(任意的,自己喜欢就好):我们增加的数据要包括数字编号、名称、next。
我们再指定一个头节点(不存放数据,只包含数字编号)。
好,我们来写一个类:1.数字编号、2.名称、3.next
这是一个类的内部类,外部的大类名为SingleLinkedListDemo
//定义PoJoNumber , 每个PoJoNumber 对象就是一个节点
class PoJoNumber{
public int nums; //数字编号
public String name; //名称
public PoJoNumber next; //指向下一个节点的指针,类型为PoJoNumber,因为下一个节点也存储的是PoJoNumber对象
public PoJoNumber(int nums, String name, PoJoNumber next) {
super();
this.nums = nums;
this.name = name;
this.next = next;
}
@Override
public String toString() {
return "PoJoNumber [nums=" + nums + ", name=" + name + ", next=" + next + "]";
}
}
add方法(排序和不排序)实现:
思路:1.先初始化一个头节点, 头节点不要动, 不存放具体的数据;
2.添加节点到单向链表 。
不考虑编号顺序时add方法 :1)找到当前链表的最后节点 ;2)将最后这个节点的next 指向 新的节点。
考虑数字编号进行自然排序addByOrder方法:1)从头节点遍历,比较节点的数字编号,从小到大进行排序;
3.head节点不能动【头节点】。
4.如果有这个排名,则添加失败,并给出提示
具体看代码实现,有详细注释:
add方法(不排序):
//add,不排序
public void add(PoJoNumber poJoNumber){
//1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
PoJoNumber temp =head;
//2.循环遍历
while(true){
//2.1不排序时,节点是添加到链表最后的,所以我们要找到链表最后,如果是最后就break
if(temp.next==null){
break;
}
temp = temp.next; //2.2如果当前节点不是最后就把下一个节点赋给当前节点,然后再接着循环遍历
}
//3.退出循环,说明已经循环到最后节点,把要新增的节点添加到最后
temp.next = poJoNumber;
}
addByOrder方法(排序):
//addByOrder,排序
public void addByOrder(PoJoNumber poJoNumber){
//1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
PoJoNumber temp =head;
boolean flag =false;//定义一个辅助标志,表示要添加的数字编号已经存在
while(true){
if(temp.next==null){ //说明已经到链表最后
break;
}
if(poJoNumber.nums
list方法实现:
//list循环show列表节点数据
public void list(){
if(head.next==null){
System.out.println("链表节点为空!");
return;
}
PoJoNumber temp =head.next; //说明头节点后面节点有数据,并赋给辅助变量temp
while(true){
if(temp==null){ //说明已经循环到链表最后
break;
}
System.out.println(temp);//输出节点的信息
temp= temp.next; //将temp后移
}
}
del删除方法实现:
//删除方法
public void del(int nums){
PoJoNumber temp =head;
boolean flag =false;
while(true){
if(temp.next==null){
break;
}
if(temp.next.nums == nums){
flag= true;
break;
}
temp = temp.next;
}
if(flag){
temp.next=temp.next.next;
}{
System.out.printf("要删除的 %d 节点不存在\n", nums);
}
}
update方法实现:
public void update(PoJoNumber poJoNumber){
PoJoNumber temp =head;
boolean flag =false;
while(true){
if(temp.next==null){
break;
}
if(temp.next.nums == poJoNumber.nums){
flag= true;
break;
}
temp = temp.next;
}
if(flag){
temp.next.name=poJoNumber.name;
}else{
System.out.printf("要修改的的 %d 节点不存在\n", poJoNumber.nums);
}
}
getLength获取链表有效节点数:
public static int getLength(PoJoNumber head){
if(head.next==null){
System.out.println("链表节点数为0");
return 0;
}
int counts=1;
PoJoNumber temp =head.next;
while(true){
if(temp.next==null){
return counts;
}
temp = temp.next;
counts++;
}
}
解题思路: 1.循环遍历获取整个链表的长度size,即上面的getLength()方法;2.获取第(size-n)个节点
public static PoJoNumber getLastIndexNode(PoJoNumber head, int index){
if(head.next==null){
System.out.println("链表为空");
return null;
}
int size = getLength(head);//第一个遍历得到链表的长度(节点个数)
if(index>size||index<=0){
System.out.println("查找倒数弟N个节点: 输入的index错误!");
return null;
}
PoJoNumber temp= head.next;
for(int i=0;i
public static void reversetList(PoJoNumber head) {
if(head.next==null||head.next.next==null){
return ;//如果当前链表为空,或者只有一个节点,无需反转,直接返回
}
PoJoNumber temp = head.next;
PoJoNumber newHead = new SingleLinkedListDemo().new PoJoNumber(0, "");
PoJoNumber newNext =null; // 指向当前节点[temp]的下一个节点
while(temp!=null){
//1.获取原链表第一个节点的后一个节点,赋值给newNext
newNext =temp.next;
//2.将新链表的元始节点,赋值给temp.next
temp.next =newHead.next;
//3.元始节点指向当前temp
newHead.next=temp;
//4.temp后移一位
temp=newNext;
}
//5.将原链表head.next指向newHead.next
head.next = newHead.next;
}
push:进栈 pop:出栈
栈:先进后出,后进先出
public static void reversePrint(PoJoNumber head){
if(head.next==null){
System.out.println("空链表!");
return;
}
PoJoNumber temp = head.next;
Stack stack = new Stack();
while(temp!=null){
stack.push(temp);
temp =temp.next;
}
while(stack.size() > 0){
System.out.println(stack.pop());
}
}
/***************************************************************************************************************************/
单链表以及面试题解答源码:
package com.study.linked;
import java.util.Stack;
/*
* 模拟链表
* 1.模拟实现链表增、删、查、改等方法;
* 2.获取链表节点个数;
* 3.获取链表倒数第N个节点
* 4.实现单链表的反转;
* 5.从尾到头打印单链表 【两种方式 方式1:反向遍历 ; 方式2:Stack栈】
*
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedListDemo demo = new SingleLinkedListDemo();
PoJoNumber member1 =demo.new PoJoNumber(1, "张三");
PoJoNumber member2 =demo.new PoJoNumber(2, "李四");
PoJoNumber member3 =demo.new PoJoNumber(3, "王麻子");
PoJoNumber member4 =demo.new PoJoNumber(4, "赵老五");
SingleLinkedList link =demo.new SingleLinkedList();
//不排序add
/* System.out.println("不排序add:");
link.add(member1);
link.add(member2);
link.add(member3);
link.add(member4);
link.list();
*/
//排序addByOrder
System.out.println("排序addByOrder:");
link.addByOrder(member1);
link.addByOrder(member4);
link.addByOrder(member3);
link.addByOrder(member2);
link.list();
link.del(4);
System.out.println("del删除4后:");
link.list();
link.update(demo.new PoJoNumber(2, "李五"));
System.out.println("update 修改2后:");
link.list();
System.out.println("链表有效节点数:"+getLength(link.getHead()));
//获取倒数第N个节点
System.out.println("获取倒数第N个节点:");
System.out.println(getLastIndexNode(link.getHead(), 2));
//将链表反转
reversetList(link.getHead());
System.out.println("链表反转后:");
link.list();
//逆序打印。栈法
System.out.println("逆序打印链表:");
reversePrint(link.getHead());
}
class SingleLinkedList{
private PoJoNumber head= new PoJoNumber(0,"");//定义一个头节点,不存放数据,只有数字编号0
public PoJoNumber getHead() { //main内获取head节点信息
return head;
}
/*******************add******************************/
//add,不排序
public void add(PoJoNumber poJoNumber){
//1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
PoJoNumber temp =head;
//2.循环遍历
while(true){
//2.1不排序时,节点是添加到链表最后的,所以我们要找到链表最后,如果是最后就break
if(temp.next==null){
break;
}
temp = temp.next; //2.2如果当前节点不是最后就把下一个节点赋给当前节点,然后再接着循环遍历
}
//3.退出循环,说明已经循环到最后节点,把要新增的节点添加到最后
temp.next = poJoNumber;
}
//addByOrder,排序
public void addByOrder(PoJoNumber poJoNumber){
//1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
PoJoNumber temp =head;
boolean flag =false;//定义一个辅助标志,表示要添加的数字编号已经存在
while(true){
if(temp.next==null){ //说明已经到链表最后
break;
}
if(poJoNumber.numssize||index<=0){
System.out.println("查找倒数弟N个节点: 输入的index错误!");
return null;
}
PoJoNumber temp= head.next;
for(int i=0;i stack = new Stack();
while(temp!=null){
stack.push(temp);
temp =temp.next;
}
while(stack.size() > 0){
System.out.println(stack.pop());
}
}
/*******************面试题目3----end******************************/
class PoJoNumber{
public int nums; //数字编号
public String name; //名称
public PoJoNumber next; //指向下一个节点的指针,类型为PoJoNumber,因为下一个节点也存储的是PoJoNumber对象
public PoJoNumber(int nums, String name) {
super();
this.nums = nums;
this.name = name;
}
@Override
public String toString() {
return "PoJoNumber [nums=" + nums + ", name=" + name + ", next=" + next + "]";
}
}
}