循环添加,有一个为空时,结束合并
找小的插到新链表
把剩下的链表直接接到新链表的尾
package com.ws.链表.单向链表.单链表面试题;
//1)求单链表中有效节点个数
//2)查找单链表中的倒数第k个节点【新浪面试题】
//3)单链表的反转【腾讯面试题】
//4)从尾到头打印单链表【百度 1.反向遍历 2.Stack栈】
//5)合并两个有序的单链表,合并之后的链表依然有序
import java.util.Stack;
//注意:在链表遍历时,要从头的下一个开始遍历:Node listNode=head.next;
// 判断是否到链表尾时是判断当前对象当前节点是否为空,以为添加了节点,要是当前节点下一个的话,就是另一个节点了
// ,而循环时已经指向下一个节点了,这是和添加节点遍历得到链表尾时是不同的
public class LinkedList {
public static void main(String[] args) {
//进行测试
//创建节点
Node node1=new Node(1,"王","小王");
Node node2=new Node(2,"李","小李");
Node node3=new Node(3,"周","小周");
Node node4=new Node(4,"马","小马");
//创建链表
NodeList nodeList=new NodeList();
//加入
nodeList.addNode(node1);
nodeList.addNode(node4);
nodeList.addNode(node2);
nodeList.addNode(node3);
//修改节点
nodeList.list();
Node node=new Node(2,"李","修改李");
nodeList.update(node);
System.out.println("修改后的链表");
//显示
nodeList.list();
//删除2节点
System.out.println("删除2节点");
nodeList.delect(2);
System.out.println("删除后");
nodeList.list();
//测试单链表的有效节点个数
System.out.println("有效地节点个数为:"+getLength(nodeList.getHead()));
//测试是否得到倒数第k个节点
System.out.println("倒数第2个节点是"+getfindk(nodeList.getHead(), 2));
//单链表的反转
System.out.println("单链表的反转");
reverList(nodeList.getHead());
nodeList.list();
//逆序打印单链表
System.out.println("逆序打印单链表,没改变链表结构");
reversPrint(nodeList.getHead());
//两个有序链表的合并
System.out.println("两个有序链表的合并");
//创建节点
Node node11=new Node(1,"王","小王");
Node node22=new Node(8,"李","小李");
Node node33=new Node(3,"周","小周");
Node node44=new Node(2,"马","小马");
Node node111=new Node(5,"2王","小王");
Node node222=new Node(4,"2李","小李");
Node node333=new Node(7,"2周","小周");
Node node444=new Node(6,"2马","小马");
//创建链表
NodeList nodeList1=new NodeList();
NodeList nodeList2=new NodeList();
//加入
nodeList1.addNode(node11);
nodeList1.addNode(node44);
nodeList1.addNode(node22);
nodeList1.addNode(node33);
nodeList2.addNode(node111);
nodeList2.addNode(node444);
nodeList2.addNode(node222);
nodeList2.addNode(node333);
System.out.println("两个新链表");
System.out.println("第一个链表");
nodeList1.list();
System.out.println("第二个链表");
nodeList2.list();
System.out.println("合并后的链表");
Node heasands=null;
try {
heasands=and(nodeList1.getHead(),nodeList2.getHead());
}catch (Exception e){
System.out.println(e.getMessage());
}
if (heasands.next==null){
System.out.println("链表为空");
}
heasands=heasands.next;
while (true){
if (heasands==null){
break;
}
System.out.println(heasands);
heasands=heasands.next;
}
}
//面试题:
//1)获取单链表的节点个数(如果带头结点的链表,需要不统计头结点)
//头节点的下一个遍历自加
public static int getLength(Node head){
//判断是否为空
if (head.next==null){
return 0;
}
int length=0;
//头的后一个节点
Node cur=head.next;
while (cur!=null){
length++;
cur=cur.next;
}
return length;
}
//2)查找单链表中的倒数第k个节点【新浪面试题】
//1.传入头结点和倒数第k个
//2.链表先从头到尾遍历,得到总长度
//3.得到长度后从头遍历(长度-k)个,得到
//4,。找到就返回,否则返回null
public static Node getfindk(Node head,int size){
//判断链表是否为空,返回null
if (head.next==null){
return null;//没有找到
}
//遍历得到链表长度
int length=getLength(head);
//遍历(length-size)位置,就是需要节点
//判断size是否合理
if (size<=0||size>length){
return null;
}
//头结点的分身遍历
Node listNode=head.next;
//循环定位到倒数的size个位置
for (int i=0;i<length-size;i++){
listNode=listNode.next;
}
return listNode;
}
//3)单链表的反转【腾讯面试题】
//遍历摘出,放到新链表的前面
//
public static void reverList(Node head){
//当前链表为空就直接返回
if (head.next==null||head.next.next==null){
//没有节点或一个节点
return;
}
//辅助变量,用于遍历,由于头节点不能动
Node listNode=head.next;//就是第一个数据节点
Node next=null;//用于固定链表,就是指向链表下一个节点,因为要移动当前节点,如果移动的话就会失去当前链表的连接,从而失去连接
Node headxin=new Node(0,"","");//新链表的头节点
//遍历原来的链表,并不断把节点取出,插到新链表的最前端
while (listNode!=null){//当前位置不为空
next=listNode.next;//保存当前节点的下一个节点
listNode.next=headxin.next;//将新链表的下一个给当前节点的下一个
headxin.next=listNode;//将当前节点给新链表的头
listNode=next;//当前节点后移
}
head.next=headxin.next;
}
//4)从尾到头打印单链表【百度 1.反向遍历 2.Stack栈】使用栈
public static void reversPrint(Node head){
if (head.next==null){
return;//空链表,不能打印
}
//创建一个栈
Stack<Node> stack=new Stack<>();
Node listNode=head.next;//头结点的副本,遍历
//将链表节点压入栈
while (listNode!=null){
stack.push(listNode);//压入栈
listNode=listNode.next;//后移,循环
}
//栈中节点打印
while (stack.size()>0){//栈中还有元素就循环
System.out.println(stack.pop());//出栈
}
}
5)合并两个有序的单链表,合并之后的链表依然有序
public static Node and(Node head1,Node head2){
//判断需要合并的链表是否为空
if (head1.next==null&&head2.next==null){
throw new RuntimeException("需要合并的两个链表为空");
}
//一条链表为空,就返回另一条链表
if (head1.next==null){
return head2;
}
if (head2.next==null){
return head1;
}
//合并后新链表的头结点
Node headand=new Node(0,"","");
//获得第一个节点数据,
//由于合并前的链表不要了,所以就直接用头结点遍历
head1=head1.next;
head2=head2.next;
//新链表头节点的副本,用于链表循环
Node listNode=headand;
//循环添加,有一个为空时,结束合并
while (head1!=null&&head2!=null){
//找最小的插到新链表
if (head1.getPaixu()<=head2.getPaixu()){
//给新链表
listNode.next=head1;
//后移节点
head1=head1.next;
}else {
listNode.next=head2;
head2=head2.next;
}
//把剩下的链表直接接到新链表的尾
if (head1==null){
listNode.next=head2;
}
if (head2==null){
listNode.next=head1;
}
//后移新链表节点
listNode=listNode.next;
}
return headand;
}
}
//定义Node,每个Node对象就是一个节点
class Node{
public int paixu;
public String name;
public String nickname;
public Node next;//指向下一个节点
//构造器
public Node(int paixu,String name,String nickname){
this.paixu=paixu;
this.name=name;
this.nickname=nickname;
}
public int getPaixu(){
return paixu;
}
@Override
public String toString() {
return "Node{" +
"paixu=" + paixu +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
//创建NodeList管理Node 就是管理节点,构建链表
//单项链表
class NodeList{
//初始化一个头节点,头结点不动
private Node head=new Node(0,"","");
public Node getHead(){//返回头结点
return head;
}
//添加节点到单向链表
//当不考虑编号顺序时
//1.找到当前链表的最后节点
//2.将最后节点的next指向新的节点
public void add(Node node){
//因为头结点不能动,所以要一个副本
Node listNode=head;
//遍历链表找到最后
while (true){
//当headtu.next=null找到链表最后
if (listNode.next==null){
break;
}
//如果没有找到最后,就将节点后移
listNode=listNode.next;
}
//退出while循环时,listNode就指向了链表最后
listNode.next=node;//将最后节点添加新链表
}
//插入到指定位置
public void addNode(Node node){
//因为头节点不能动,所以要有一个副本找节点
//找的listNode是在添加位置的前一个节点,否则插不进去,因为要node.next=list.next
Node listNode=head;
boolean flag=false;//标志,添加的编号是否已经存在,默认false 就是新节点编号是否已经存在
while (true){
if (listNode.next==null){
//说明listNode已经在链表最后
break;
}
//由小到大排序,后一个比前一个大就插入后一个的前面
//因为要插入到添加位置的前面
if (listNode.next.paixu>node.paixu){//位置找到
break;
}else if (listNode.next.paixu==node.paixu){
//说明希望添加的序号已经存在
flag=true;//编号已经存在
break;
}
listNode=listNode.next;//后移,遍历链表
}
//判断编号是否已经存在
if (flag){//找到
System.out.printf("添加编号【%d】已经存在,不能加入\n",listNode.next.paixu);
}else {
//插入到链表中
node.next=listNode.next;
listNode.next=node;
}
}
//修改节点信息
//根据序号修改,序号不能更改
public void update(Node node){
//判断链表是否为空
if (head.next==null){
System.out.println("链表为空");
return;
}
//根据序号找到需要修改的节点
//一个头节点的副本
Node listNode=head;
boolean flag=false;//表示是否找到该节点
while (true){
if (listNode==null){
//到链表的尾,啥也没找到
break;
}
if (listNode.paixu==node.paixu){
//已经找到节点
flag=true;
break;
}
listNode=listNode.next;
}
//判断是否找到要修改的节点
if (flag){//找到
listNode.name=node.name;
listNode.nickname=node.nickname;
}else {
//没有找到节点
System.out.printf("没有找到编号【%d】的节点,不能修改\n",node.paixu);
}
}
//删除节点
//找到要删除的节点的前一个节点
//因为要listNode.next=listNode.next.next
//被删除的节点将被垃圾回收机制删除
public void delect(int paixu){
//头不能动,要有一个副本
Node listNode=head;
boolean flag=false;//是否找到待删除的前一个节点
while (true){
if (listNode.next==null){
//已经到链表尾,啥也没找打
break;
}
if (listNode.next.paixu==paixu){
//找到待删除节点前一个
flag=true;
break;
}
listNode=listNode.next;
}
//判断是否找到待删除节点
if (flag){
//可以删除
listNode.next=listNode.next.next;
}else {
System.out.printf("要删除的【%d】节点不存在\n",paixu);
}
}
//遍历单向链表
public void list(){
//判断链表是否为空
if (head.next==null){
System.out.println("链表为空");
return;
}
//因为头节点不能动,所以要一个副本来遍历
Node listNode=head.next;
while (true){
//判断是否到链表最后
if (listNode==null){
break;
}
//链表不为空,输出节点信息
System.out.println(listNode);
//将节点后移listNode
listNode=listNode.next;
}
}
}