单链表
-
基本介绍
<1> 以节点的方式存储 是链式存储
<2> 每个节点包含next date
<3> 链表的各个节点不一定是连续存储的
<4> 类型 ①带头节点 ②不带头节点
-
单链表的应用
public class HeroNode {
int no;
String name;
String nickname;
HeroNode next;
public HeroNode(int no,String name,String nickname){
this.no=no;
this.name=name;
this.nickname=nickname;
}
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
}
ublic class SingleLinkedList {
//先初始化一个头节点 头节点不存放数据
private HeroNode head = new HeroNode(0,"","");
/**
* 添加元素到单向链表尾部
* @param heroNode
*/
//1.通过辅助变量找到链表最后一个元素
//2.直接添加到最后即可
public void add(HeroNode heroNode){
HeroNode t = head;
while(true){
if(t.next==null){
break;
}
t=t.next;
}
t.next=heroNode;
}
/**
* 根据编号添加到链表中
* @param heroNode
*/
public void addByOrder(HeroNode heroNode){
HeroNode t = head;
boolean flag=false;//表示添加的编号是否存在 若编号存在 则提示错误
while(true){
if(t.next==null){//说明t已经在链表最后
break;
}
if(t.next.no>heroNode.no){
break;
}else if(t.next.no==heroNode.no){
flag=true;
break;
}
t=t.next;
}
if(flag){
System.out.println("该编号已存在!");
}else{
heroNode.next=t.next;
t.next=heroNode;
}
}
/**
* 修改节点信息
* @param newHeroNode
*/
public void update(HeroNode newHeroNode){
if(head.next==null){
System.out.println("链表空!");
return;
}
HeroNode t=head.next;
boolean flag=false;
while(true){
if(t==null){//说明t已经在链表最后
break;
}
if(t.no==newHeroNode.no){
flag=true;
break;
}
t=t.next;
}
if(flag){
t.name=newHeroNode.name;
t.nickname=newHeroNode.nickname;
}else{
System.out.println("该节点不存在!");
}
}
/**
* 删除节点
* @param no
*/
public void del(int no){
HeroNode t=head;
boolean flag=false;
while(true){
if(t.next==null){//说明t已经在链表最后
break;
}
if(t.next.no==no){
flag=true;
break;
}
t=t.next;
}
if(flag){
t.next=t.next.next;
}else{
System.out.println("该节点不存在!");
}
}
/**
* 显示链表
*/
public void list(){
if(head.next==null){
System.out.println("链表空!");
return;
}
HeroNode t=head.next;
while(true){
if(t==null){
break;
}
System.out.println(t);
t=t.next;
}
}
//面试题-------------------------------
/**
* 获取单链表节点个数(带头结点的不算头节点)
* 遍历链表 统计个数
* @param head
* @return
*/
public int getLength(HeroNode head){
HeroNode cur=head.next;//定义一个辅助变量
int length=0;
if(head.next==null){
return 0;
}
while(cur!=null){
length++;
cur=cur.next;
}
return length;
}
/**
* 查找单链表中倒数第index个节点
* 1)统计节点个数 size
* 2)从头开始找到第 (size-index)个
* @param head
* @param index
* @return
*/
public HeroNode findLastIndexNode(HeroNode head,int index){
int size = getLength(head);
HeroNode cur = head.next;
if(head.next==null){
return null;
}
if(index<0||index>size){
return null;
}
for(int i=1;i<=(size-index);i++){
cur=cur.next;
}
return cur;
}
/**
* 单链表的反转
* 1)遍历原链表 每遍历一个元素就插入到新链表的最前端
* 2)原表头指针指向新表头指针
* @param head
*/
public void reversetList(HeroNode head){
//空表或者只有一个元素不需要反转
if(head.next==null||head.next.next==null){
return;
}
HeroNode reverseHead = new HeroNode(0, "", "");
HeroNode cur = head.next;
HeroNode next=null;
while(cur!=null){
next=cur.next;//暂时保存原链表的第二个节点
cur.next=reverseHead.next;//保存在新链表的最前端
reverseHead.next=cur;//cur后移
cur=next;
}
head.next=reverseHead.next;
}
/**
* 从未到头打印单链表—
* 1)通过反转——不推荐 改变了原链表 且当链表过大效率低,浪费空间
* 2)通过栈实现——先进后出
* 遍历链表 将节点添加到栈中,在出栈
* @param head
*/
public void reversePrint(HeroNode head){
if(head.next==null||head.next.next==null){
return;
}
Stack<HeroNode> stack = new Stack<HeroNode>();
HeroNode cur = head.next;
while(cur!=null){
stack.add(cur);
cur=cur.next;
}
while(stack.size()>0){
System.out.println(stack.pop());
}
}
双链表
-
单链表的缺点分析
<1> 单链表的查找方向只能是一个方向,而双链表可以向前或向后查找
<2> 单链表不能自我删除,需要依靠辅助节点,并且要找到删除节点的前一节点 -
双链表的应用
public class DoubleLinkedList {
//初始化一个头节点
HeroNode2 head= new HeroNode2(0, "", "");
/**
* 添加元素到单向链表尾部
* @param heroNode
*/
//1.通过辅助变量找到链表最后一个元素
//2.直接添加到最后即可
public void add(HeroNode2 heroNode){
HeroNode2 t = head;
while(true){
if(t.next==null){
break;
}
t=t.next;
}
t.next=heroNode;
heroNode.pre=t;
}
/**
* 根据编号添加到链表中
* @param heroNode
*/
public void addByOrder(HeroNode2 heroNode){
HeroNode2 t = head;
boolean flag=false;//表示添加的编号是否存在 若编号存在 则提示错误
while(true){
if(t.next==null){//说明t已经在链表最后
break;
}
if(t.next.no>heroNode.no){
break;
}else if(t.next.no==heroNode.no){
flag=true;
break;
}
t=t.next;
}
if(flag){
System.out.println("该编号已存在!");
}else{
heroNode.next=t.next;
if(t.next!=null){
t.next.pre=heroNode;
}
t.next=heroNode;
heroNode.pre=t;
}
}
/**
* 修改节点信息
* @param newHeroNode
*/
public void update(HeroNode2 newHeroNode){
if(head.next==null){
System.out.println("链表空!");
return;
}
HeroNode2 t=head.next;
boolean flag=false;
while(true){
if(t==null){//说明t已经在链表最后
break;
}
if(t.no==newHeroNode.no){
flag=true;
break;
}
t=t.next;
}
if(flag){
t.name=newHeroNode.name;
t.nickname=newHeroNode.nickname;
}else{
System.out.println("该节点不存在!");
}
}
/**
* 删除节点
* @param no
*/
public void del(int no){
HeroNode2 t=head.next;
boolean flag=false;
while(true){
if(t==null){//说明t已经在链表最后
break;
}
if(t.no==no){
flag=true;
break;
}
t=t.next;
}
if(flag){
t.pre.next=t.next;
//注意空指针异常
if(t.next!=null){
t.next.pre=t.pre;
}
}else{
System.out.println("该节点不存在!");
}
}
/**
* 显示链表
*/
public void list(){
if(head.next==null){
System.out.println("链表空!");
return;
}
HeroNode2 t=head.next;
while(true){
if(t==null){
break;
}
System.out.println(t);
t=t.next;
}
}
}
单向环形链表
- 基本介绍
<1> 最后一个节点不是null,而指向头节点 - 环形链表的应用
问题:编号为1,2…,N的N个小孩围成一圈,编号为K的人从1开始报数, 数到M的人出列,从出列的人再次开始报数,数到M的人出列,以此类推, 直到所有人都出列为止,产生一个出队编号的序列
public class Boy {
public Boy(int no) {
this.no=no;
}
public int no;
public Boy next;
}
public class CircleSingleLinkedList {
Boy first = null;
/**
* 添加节点
* @param nums 最初的人数
*/
public void addBoy(int nums){
if(nums<1){
System.out.println("nums错误!");
return;
}
Boy curBoy = null;
for(int i=1;i<=nums;i++){
Boy boy = new Boy(i);
//第一个学生
if(i==1){
first=boy;
first.next=boy;
curBoy=first;
}else{
curBoy.next=boy;
curBoy=boy;
curBoy.next=first;
}
}
}
/**
* 显示环形链表
*/
public void showBoy(){
if(first==null){
System.out.println("没有人!");
return;
}
Boy curBoy = first;
while(true){
System.out.print(curBoy.no+"\t");
curBoy=curBoy.next;
if(curBoy==first){
break;
}
}
System.out.println();
}
/**
* 计算出圈顺序
* @param startNo 从第几个小孩开始
* @param countNum 数几下
* @param nums 最初有多少个小孩
*/
public void countBoy(int startNo,int countNum,int nums){
Boy helper =first;//辅助节点 指向first前一个点
if(first==null||startNo<1||startNo>countNum){
System.out.println("参数有误!");
return;
}
//helper最初指向最后一个点
while(true){
if(helper.next==first){
break;
}
helper=helper.next;
}
//System.out.println(helper.no);
//移动到开始报数的那个点
for(int i=1;i<=startNo-1;i++){
first=first.next;
helper=helper.next;
}
//System.out.println(first.no);
//System.out.println(helper.no);
System.out.println("出圈顺序:");
while(nums>0){//判断条件也可以改为first=helper
//开始报数 first移动到要被移除的那个点
for(int i=1;i<=countNum-1;i++){
first=first.next;
helper=helper.next;
}
//System.out.println(first.no);
//System.out.println(helper.no);
System.out.print(first.no+"\t");
//移除first点
helper.next=first.next;
first=first.next;
nums--;
}
}
}