2.单链表
1.链表介绍
链表是有序的列表,但是他在内存中的存储如下:
。
小结:
- 链表是以节点的方式存储
- 每个节点包含data域和next域(指向下一个节点)
- 如图:发现每个节点并不是一定连续的
- 链表分带头节点的链表和没有头节点的链表,根据实际情况而定
场景:创建一个链表
链表新增:
package cn.com.linkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList singleLinkedList = new SingleLinkedList();
HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
singleLinkedList.add(heroNode1);
singleLinkedList.add(heroNode2);
singleLinkedList.add(heroNode3);
singleLinkedList.add(heroNode4);
singleLinkedList.list();
}
}
//管理英雄
class SingleLinkedList {
//1.定义一个头元素,只是表明头元素
HeroNode head = new HeroNode(0, "", "");
/**
* 添加
*
* @param node
*/
public void add(HeroNode node) {
//定义一个临时变量,因为head不能变化,所以定义一个中间变量
HeroNode tmp = head;
//找到队列的最后一个元素,最后一个元素的next=null
while (tmp.getNext() != null) {
tmp = tmp.getNext();
}
tmp.setNext(node);
}
/**
* 遍历输出
*/
public void list() {
if (head.getNext() == null) {
System.out.println("链表为空!");
return;
}
HeroNode tmp = head.getNext();
//找到队列的最后一个元素,最后一个元素的next=null
while (true) {
if (tmp == null) {
break;
}
System.out.println(tmp.toString());
tmp = tmp.getNext();
}
}
}
class HeroNode {
//英雄编号
private int no;
//英雄名称
private String name;
//艺名
private String nickName;
//下一个
private HeroNode next;
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public HeroNode getNext() {
return next;
}
public void setNext(HeroNode next) {
this.next = next;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
package cn.com.linkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList singleLinkedList = new SingleLinkedList();
HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
singleLinkedList.add(heroNode1);
singleLinkedList.add(heroNode2);
singleLinkedList.add(heroNode3);
singleLinkedList.add(heroNode4);
singleLinkedList.list();
}
}
//管理英雄
class SingleLinkedList {
//1.定义一个头元素,只是表明头元素
HeroNode head = new HeroNode(0, "", "");
/**
* 添加
*
* @param node
*/
public void add(HeroNode node) {
//定义一个临时变量,因为head不能变化,所以定义一个中间变量
HeroNode tmp = head;
//找到队列的最后一个元素,最后一个元素的next=null
while (tmp.getNext() != null) {
tmp = tmp.getNext();
}
tmp.setNext(node);
}
/**
* 遍历输出
*/
public void list() {
if (head.getNext() == null) {
System.out.println("链表为空!");
return;
}
//head一定不为空,tmpp就是第一个有效数据
HeroNode tmp = head.getNext();
//找到队列的最后一个元素,最后一个元素的next=null
while (true) {
if (tmp == null) {
break;
}
System.out.println(tmp.toString());
tmp = tmp.getNext();
}
上述判断也可以这么写:
HeroNode tmp = head;
//找到队列的最后一个元素,最后一个元素的next=null
while ((tmp=tmp.getNext())!=null) {
System.out.println(tmp.toString());
}
}
}
class HeroNode {
//英雄编号
private int no;
//英雄名称
private String name;
//艺名
private String nickName;
//下一个
private HeroNode next;
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public HeroNode getNext() {
return next;
}
public void setNext(HeroNode next) {
this.next = next;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
输出:
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
问题:上面的英雄没有按照no排序的功能,例如:
public static void main(String[] args) {
SingleLinkedList singleLinkedList = new SingleLinkedList();
HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
singleLinkedList.add(heroNode1);
//重点1:将英雄4放在英雄1后面添加
singleLinkedList.add(heroNode4);
singleLinkedList.add(heroNode2);
singleLinkedList.add(heroNode3);
singleLinkedList.list();
}
输出:发现并没有按照no排名
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
第二种方式:在添加英雄是,根据no将英雄插入到指定位置,如果队列中已有这个no,则添加失败,给出提示
/**
* 按照顺序添加
*/
public void addByOrder(HeroNode node) {
HeroNode temp=head;
//遍历得到node需要插入的前一个节点
while (true){
if (temp.getNext()==null){
break;
}
//必须找到插入处的上一个节点,这里必须用temp.getNext.getNo和no判断,不能用temp.getno和no判断,细品!!
if (temp.getNext().getNo()==node.getNo()){
System.out.println("列表中红已有该no的节点,no:"+node.getNo());
return;
}else if(temp.getNext().getNo()>node.getNo()){
//找到了需要插入的节点,即当前tmp节点
break;
}
temp=temp.getNext();
}
//1.必须先设置node的后一个节点,因为如果先设置temp.setNext,这里的node.setNext(temp.getNext)获取的就是自己
node.setNext(temp.getNext());
temp.setNext(node);
}
测试:
SingleLinkedList singleLinkedList = new SingleLinkedList();
HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
/*singleLinkedList.add(heroNode1);
singleLinkedList.add(heroNode4);
singleLinkedList.add(heroNode2);
singleLinkedList.add(heroNode3);
singleLinkedList.list();*/
singleLinkedList.addByOrder(heroNode1);
//重点1:此处先+4节点
singleLinkedList.addByOrder(heroNode4);
singleLinkedList.addByOrder(heroNode2);
singleLinkedList.addByOrder(heroNode3);
singleLinkedList.list();
发现输出:依旧按照no顺序输出
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
单链表的修改
/**
* 修改
*
* @param node 新节点
*/
public void update(HeroNode node) {
HeroNode temp = head.getNext();
//是否找到需要更新的节点
boolean flag = true;
while (true) {
if (temp == null) {
flag = false;
break;
}
if (temp.getNo() == node.getNo()) {
break;
}
temp = temp.getNext();
}
if (flag){
//这里可以用直接setnext的当时去替换节点,但是替换下来的节点会是垃圾对象,需要考虑下垃圾回收问题,太多是否会造成oom
temp.setName(node.getName());
temp.setNickName(node.getNickName());
}
}
测试:
singleLinkedList.addByOrder(heroNode1);
singleLinkedList.addByOrder(heroNode4);
singleLinkedList.addByOrder(heroNode2);
singleLinkedList.addByOrder(heroNode3);
singleLinkedList.list();
HeroNode heroNode5 = new HeroNode(4, "林冲--", "豹子头--");
System.out.println("-----------------------");
singleLinkedList.update(heroNode5);
singleLinkedList.list();
输出:发现已经更新成功
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
-----------------------
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲--', nickName='豹子头--'}
单链表的删除,根据no删除具体的节点:
/**
* 根据编号删除,难点是,因为节点没有获取上一个节点的方法,所以需要定位到需要删除节点的上一个节点处
*
* @param no
*/
public void delete(int no) {
HeroNode temp = head;
//找到需要删除的上一个节点
boolean flag = true;
while (true) {
if (temp.getNext() == null) {
flag = false;
break;
}
//tmp就是需要删除节点的上一个节点
if (temp.getNext().getNo() == no) {
break;
}
temp = temp.getNext();
}
if (flag) {
temp.setNext(temp.getNext().getNext());
}else{
System.out.println("删除失败,链表中没有该节点:"+no);
}
}
测试:
//删除节点
System.out.println("删除节点后----------------");
singleLinkedList.delete(2);
singleLinkedList.list();
输出:发现2节点已被删除
删除节点后----------------
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲--', nickName='豹子头--'}
单链表面试
1.求单链表中有效节点的个数
/**
* 获取长度
*
* @return
*/
public int getLength() {
if (head.getNext() == null) {
return 0;
}
HeroNode temp = head.getNext();
int length = 0;
while (temp!=null) {
temp = temp.getNext();
length++;
}
return length;
}
2.查找单链表中倒数第k个节点
分析,先得到有效节点个数size,再正向遍历size-k
/**
* 获取长度
*
* @return
*/
public int getLength() {
if (head.getNext() == null) {
return 0;
}
HeroNode temp = head.getNext();
int length = 0;
while (temp != null) {
temp = temp.getNext();
length++;
}
return length;
}
/**
* 获得倒数第几个节点
*
* @param index 倒数第几个
* @return
*/
public HeroNode getLastIndexNode(int index) {
//获取有效节点个数
int size=getLength();
if(size==0){
System.out.println("链表为空");
return null;
}
if (index<0||index>size){
System.out.println("index输入有误");
return null;
}
int count=1;
HeroNode temp=head.getNext();
while (count!=(size-(index-1))){
temp=temp.getNext();
count++;
}
return temp;
}
测试:
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲--', nickName='豹子头--'}
------------
有效长度:3
倒数第2个元素是:HeroNode{no=3, name='吴用', nickName='智多星'}
3.单链表的反转
public void reserve() {
if (head.getNext() == null || head.getNext().getNext() == null) {
System.out.println("列表为空,或者列表只有一个元素,不需要反转!");
}
//定义一个新的头部
HeroNode newHead = new HeroNode(0, "", "");
//当前节点
HeroNode temp = head.getNext();
//下一个节点
HeroNode next=null;
while (temp != null) {
next=temp.getNext();
temp.setNext(newHead.getNext());
newHead.setNext(temp);
temp=next;
}
head.setNext(newHead.getNext());
}
输出:
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
反转------------
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=1, name='宋江', nickName='及时雨'}
4.从尾到头的打印链表
/**
* 反转打印
*/
public void resevePrint(){
if (head.getNext()==null){
System.out.println("链表为空!");
}
HeroNode temp=head.getNext();
Stack<HeroNode> stack=new Stack<HeroNode>();
while (temp!=null){
stack.push(temp);
temp=temp.getNext();
}
while (stack.size()>0){
System.out.println(stack.pop());
}
}
测试输出:
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=1, name='宋江', nickName='及时雨'}
反转打印-----------
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
5.合并两个有序链表,合并以后依旧有序
预留!