1、链表概念及应用
1.1、概念
链表是以节点的方式来存储,是链式存储
每个节点包含 data 域, next 域:指向下一个节点.
发现链表的各个节点不一定是连续存储.
链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
1.2、链表应用-增删改查
使用带head头的单向链表实现 –水浒英雄排行榜管理
1、完成对英雄人物的增删改查操作, 注: 删除和修改,查找
可以考虑学员独立完成,也可带学员完成
2、第一种方法在添加英雄时,直接添加到链表的尾部
3、第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
1.2.1、data类
public class Data {private int no;private String name;private String nickName;public Data(int no, String name, String nickName) {this.no = no;this.name = name;this.nickName = nickName;}public Data() {}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;}@Overridepublic String toString() {return "Data{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';}}
1.2.2、Node类
public class Node {private Data data;private Node next;public Node() {}public Node(Data data) {this.data = data;}public Node(Data data,Node next) {this.data = data;this.next=next;}public Data getData() {return data;}public void setData(Data data) {this.data = data;}public Node getNext() {return next;}public void setNext(Node next) {this.next = next;}@Overridepublic String toString() {return "Node{" +"data=" + data +'}';}}
1.2.3、SingleLinkedList类
public class SingleLinkedList {private Node head=new Node(null,null);public void setHead(Node head){this.head=head;}public Node getHead(){return head;}//添加节点,不考虑排序public void insertNode(Node node){if(node.getData()==null) System.out.println("无效的节点");if(head.getNext()==null){head.setNext(node);System.out.println("添加结点成功");return;}Node temp=head;while (true){if(temp.getNext()==null){temp.setNext(node);System.out.println("添加结点成功");break;}temp=temp.getNext();}}//排序添加节点且不允许相同no的节点public void insertNodeByOrder(Node node){if(node.getData()==null) System.out.println("无效的节点");if(head.getNext()==null){head.setNext(node);return;}Node temp=head;while(true){if(temp.getNext()==null){temp.setNext(node);break;}else if(temp.getNext().getData().getNo()==node.getData().getNo()){System.out.println("存在相同编号的节点"+temp.getNext());break;}else if(temp.getNext().getData().getNo()>node.getData().getNo()){node.setNext(temp.getNext());temp.setNext(node);break;}temp=temp.getNext();}}//通过编号修改昵称public void updateNodeByNo(int no,String nickName){if(head.getNext()==null){System.out.println("链表为空");return;}Node temp=head;while(true){if(temp.getNext()==null){System.out.println("没有找到该编号的节点信息");break;}else if(temp.getNext().getData().getNo()==no){temp.getNext().getData().setNickName(nickName);break;}temp=temp.getNext();}}//通过编号删除节点public void deleteNode(int no){if(head.getNext()==null){System.out.println("链表为空");return;}Node temp=head;while(true){if(temp.getNext()==null){System.out.println("没有找到该编号的节点");break;}else if(temp.getNext().getData().getNo()==no) {temp.setNext(temp.getNext().getNext());break;}temp=temp.getNext();}}//获取链表的长度public int getLength(){if(head.getNext()==null){System.out.println("链表为空");System.exit(0);}Node temp=head;int result=0;while (true){if(temp.getNext()==null){break;}result++;temp=temp.getNext();}return result;}//打印链表节点public void printNode(){if(head.getNext()==null){System.out.println("链表为空");return;}Node temp=head;while (true){if(temp.getNext()==null){break;}temp=temp.getNext();System.out.println(temp);}}}
2、单链表面试题
单链表的常见面试题有如下:
1、求单链表中有效节点的个数
2、查找单链表中的倒数第k个结点 【新浪面试题】
3、单链表的反转【腾讯面试题,有点难度】
4、从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】
5、合并两个有序的单链表,合并之后的链表依然有序【课后练习.】
2.1、新浪-找到单数第k个节点
//查找链表倒数第index个元素public static Node getLastIndexNode(Node head, int index) {if (head.getNext() == null) {System.out.println("链表为空");return null;}SingleLinkedList singleLinkedList = new SingleLinkedList();singleLinkedList.setHead(head);int length = singleLinkedList.getLength();if (index <= 0 || index > length) return null;Node temp=head;int curNodeIndex=0;while(true){if(curNodeIndex==(length-index)+1){return temp;}temp=temp.getNext();curNodeIndex++;}}
2.2、腾讯-反转链表
//反转链表,头插法public static void reverseLinkedList(Node head){if(head.getNext()==null){System.out.println("空链表");return;}if(head.getNext().getNext()==null){System.out.println("当前链表不需要进行反转");return;}Node curNode=head.getNext();Node nextNode=head.getNext().getNext();curNode.setNext(null);head.setNext(curNode);while(true){if(nextNode==null){return;}curNode=nextNode;nextNode=nextNode.getNext();curNode.setNext(head.getNext());head.setNext(curNode);}}
2.3、百度-从尾到头打印链表
//从为到头打印链表public static void printNodeFromTailToHead(Node head){if(head.getNext()==null){System.out.println("空链表");return;}if(head.getNext().getNext()==null){System.out.println(head.getNext());return;}SingleLinkedList singleLinkedList = new SingleLinkedList();singleLinkedList.setHead(head);int length = singleLinkedList.getLength();Node[] nodes=new Node[length];int cur=length-1;Node temp=head;while(true){if (temp.getNext()==null){break;}temp=temp.getNext();nodes[cur--]=temp;}for (Node node : nodes) {System.out.println(node);}}
2.4、合并链表并保持有序
3、双向链表-增删改查
3.1、Node类
public class Node {private Data data;private Node next;private Node pre;public Node() {}public Node(Data data) {this.data = data;}public Node(Data data,Node next) {this.data = data;this.next=next;}public Data getData() {return data;}public void setData(Data data) {this.data = data;}public Node getPre() {return pre;}public void setPre(Node pre) {this.pre = pre;}public Node getNext() {return next;}public void setNext(Node next) {this.next = next;}@Overridepublic String toString() {return "Node{" +"data=" + data +'}';}}
3.2、DoubleLinkedList类-增删改查
public class DoubleLinkedList {private Node head=new Node(null,null);private Node pre;private Node next;public Node getHead() {return head;}public void setHead(Node head) {this.head = head;}public Node getPre() {return pre;}public void setPre(Node pre) {this.pre = pre;}public Node getNext() {return next;}public void setNext(Node next) {this.next = next;}@Overridepublic String toString() {return "DoubleLinkedList{" +"head=" + head +", pre=" + pre +", next=" + next +'}';}//插入节点,不允许相同节点且排序public void insertNode(Node node){if(node.getData()==null||node==null)return;if(head.getNext()==null){head.setNext(node);node.setPre(head);return;}Node temp=head;while(true){if(temp.getNext()==null){temp.setNext(node);node.setPre(temp);break;}else if(temp.getNext().getData().getNo()>node.getData().getNo()){node.setNext(temp.getNext());temp.getNext().setPre(node);temp.setNext(node);break;}else if(temp.getNext().getData().getNo()==node.getData().getNo()){System.out.println("存在相同编号的节点,无法添加"+temp.getNext());break;}temp=temp.getNext();}}//根据id更新昵称信息public void updateNodeByNo(int no,String nickName){if(head.getNext()==null){System.out.println("链表为空");return;}Node temp=head;while(true){if(temp.getNext()==null){System.out.println("没有找到该编号的节点信息");break;}else if(temp.getNext().getData().getNo()==no){temp.getNext().getData().setNickName(nickName);break;}temp=temp.getNext();}}//根据id删除节点public void deleteNode(int no){if(head.getNext()==null){System.out.println("链表为空");return;}Node temp=head;while(true){if(temp.getNext()==null){System.out.println("没有找到该编号的节点");break;}else if(temp.getNext().getData().getNo()==no) {temp.getNext().getNext().setPre(temp);temp.setNext(temp.getNext().getNext());break;}temp=temp.getNext();}}//打印双向链表public void printDoubleList(){if(head.getNext()==null){System.out.println("链表为空");return;}Node temp=head;while (true){if(temp.getNext()==null){break;}temp=temp.getNext();System.out.println(temp);}}}
4、环形链表-增删改查

Josephu 问题
Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
