链表
一.链表简介
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
二.基本概念
链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作链表的每个元素称为一个节点,每个节点都可以存储在内存中的不同的位置,为了表示每个元素与后继元素的逻辑关系,以便构成“一个节点链着一个节点”的链式存储结构,除了存储元素本身的信息外,还要存储其直接后继信息,因此,每个节点都包含两个部分,第一部分称为链表的数据区域,用于存储元素本身的数据信息,这里用data表示,它不局限于一个成员数据,也可是多个成员数据,第二部分是一个结构体指针,称为链表的指针域,用于存储其直接后继的节点信息,这里用next表示,next的值实际上就是下一个节点的地址,当前节点为末节点时,next的值设为空指针。
三.代码实现
1.定义HeroNode 链表结点对象
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; } public HeroNode() { } @Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickname='" + nickname + '\'' + '}'; } }
2.定义SingleLinkedList:单向链表
public class SingleLinkedList { private HeroNode head = new HeroNode(0,"","");//初始化头结点不存放数据 public HeroNode getHead() { return head; } public void setHead(HeroNode head) { this.head = head; } /*添加数据不考虑顺序*/ public void add(HeroNode heroNode){ //找到链表的最后一个位置 HeroNode temp = head; while (true){ if (temp.next == null){//找到链表的最后位置 break; } //如果没有找到将指针temp后移 temp = temp.next; } //当循环结束时,指针找到链表的最后的位置 temp.next = heroNode; } /*显示链表*/ public void list(){ //判断链表是否为空 if (head.next == null){ System.out.println("链表为空"); return; } //头结点不能动需要辅助指针进行遍历 HeroNode temp = head.next; while (true){ if (temp == null){//找到链表的最后位置 break; } System.out.println(temp);//输出 temp = temp.next;//temp后移 } } /*根据排名顺序进行添加*/ public void addByOrder(HeroNode heroNode){ HeroNode temp = head; boolean flag = false; //添加的编号是否存在 while (true){//遍历链表 if (temp.next == null){ break; } if (temp.next.no > heroNode.no){//位置找到在指针后添加 break; }else if (temp.next.no == heroNode.no){//已经存在 flag = true; break; } temp = temp.next; } //判断flag值 if (flag){//说明编号存在 System.out.printf("准备插入的数据编号%d已经存在\n",heroNode.no); }else { //加入到链表中指针后面 heroNode.next = temp.next; temp.next = heroNode; } } /*修改节点数据根据编号进行修改*/ public void update(HeroNode heroNode){ if (head.next ==null){//判断链表是否为空 System.out.println("链表为空,不能修改"); return; } //进行修改 HeroNode temp = head.next; boolean flag = false; //添加的编号是否存在 while (true){ if (temp ==null){ break; } if (temp.no == heroNode.no){ flag = true; break; } temp = temp.next; } if (flag){ temp.name = heroNode.name; temp.nickname = heroNode.nickname; }else { System.out.printf("没有找到编号%d的节点,不能修改\n",heroNode.no); } } /*删除节点*/ public void del(int no){ if (head.next == null){//判断链表是否为空 System.out.println("链表为空,不能修改"); return; } HeroNode temp=head; boolean flag=false; while (true){ if (temp.next ==null){ break; } if (temp.next.no==no){ flag=true; break; } temp=temp.next; } if (flag){ temp.next=temp.next.next; }else { System.out.printf("没有找到编号%d节点的数据",no); } } }
3.进行测试
public class SingleLinkedListDemo { public static void main(String[] args) { //进行初始 HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨"); HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟"); HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星"); HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头"); SingleLinkedList singleLinkedList = new SingleLinkedList(); //加入 singleLinkedList.add(heroNode1); singleLinkedList.add(heroNode4); singleLinkedList.add(heroNode2); singleLinkedList.add(heroNode3); //加入按照编号的顺序 /* singleLinkedList.addByOrder(heroNode1); singleLinkedList.addByOrder(heroNode4); singleLinkedList.addByOrder(heroNode3); singleLinkedList.addByOrder(heroNode2);*/ // singleLinkedList.addByOrder(heroNode3); //显示 /*singleLinkedList.list();*/ //修改节点 /*System.out.println("修改后为++++++++++"); HeroNode heroNode5 = new HeroNode(3, "林冲1", "豹子头1"); singleLinkedList.update(heroNode5); singleLinkedList.list();*/ //删除 /*singleLinkedList.del(2); System.out.println("删除后++++++++"); singleLinkedList.list();*/ //获取到链表的有效节点的个数 // System.out.println(SingleLinkedListDemo.getLength(singleLinkedList.getHead())); //查找链表倒数第K个节点 System.out.println(SingleLinkedListDemo.findLastIndex(singleLinkedList.getHead(),2)); //链表反正 //System.out.println("反转之前+++++"); //singleLinkedList.list(); //System.out.println("反转之后+++++"); //reversetList(singleLinkedList.getHead()); singleLinkedList.list(); System.out.println("逆序遍历++++++"); SingleLinkedListDemo.reversePrint(singleLinkedList.getHead()); } /*方法:获取到链表的有效节点的个数*/ public static int getLength(HeroNode head){ int count=0; if (head.next==null){ return 0; } HeroNode cur = head.next; while (cur!=null){ count++; cur=cur.next; } return count; } /*方法:查找链表倒数第K个节点*/ //1.编写方法:接收header节点,同时接收一个倒数第index个// public static HeroNode findLastIndex(HeroNode head,int index){ if (head.next==null){ return null; } //得到链表的长度 int size = getLength(head); //第二次遍历到size-index //数据校验 if (index<=0||index>size){ return null; } HeroNode cur = head.next; for (int i = 0; i <size-index ; i++) { cur = cur.next; } return cur; } /*链表反正转*/ public static void reversetList(HeroNode head){ //如果链表为空或者只有一个节点不需要反转 if (head.next==null||head.next.next==null){ return; } HeroNode cur = head.next; HeroNode next = null; HeroNode reverseHead=new HeroNode(0,"",""); while (cur!=null){ next = cur.next;//暂时保存当地节点的下一个节点 cur.next=reverseHead.next;//将cur的下一个节点指向反转链表的头部 reverseHead.next=cur; cur=next; } //head.next指向reverseHead.next实现链表反转 head.next=reverseHead.next; } /*逆向遍历*/ public static void reversePrint(HeroNode head){ if (head.next==null){ return; } //创建一个栈将节点压入栈中 Stack<HeroNode> stack =new Stack<>(); HeroNode cur = head.next; while (cur!=null){ stack.push(cur); cur=cur.next; } //将栈中的数据进行打印 while (stack.size()>0){ System.out.println(stack.pop()); } } }
3、链表与数组比较 数组(包括结构体数组)的实质是一种线性表的顺序表示方式,它的优点是使用直观,便于快速、随机地存取线性表中的任一元素,但缺点是对其进行 插入和删除操作时需要移动大量的数组元素,同时由于数组属于静态内存分配,定义数组时必须指定数组的长度,程序一旦运行,其长度就不能再改变,实际使用个数不能超过数组元素最大长度的限制,否则就会发生下标越界的错误,低于最大长度时又会造成系统资源的浪费,因此空间效率差 四.双向链表 1.双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。 代码实现: 1定义结点
public class HeroNode2 { int no; String name; String nickname; HeroNode2 next;//指向下一个节点 HeroNode2 pre;//指向上一个节点 public HeroNode2(int no, String name, String nickname) { this.no = no; this.name = name; this.nickname = nickname; } public HeroNode2() { } @Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickname='" + nickname + '\'' + '}'; } }
2.定义DoubleLinkedList 双向链表
public class DoubleLinkedList { private HeroNode2 head = new HeroNode2(0,"","");//初始化头结点不存放数据 public HeroNode2 getHead() { return head; } /*遍历双向链表*/ public void list(){ //判断链表是否为空 if (head.next == null){ System.out.println("链表为空"); return; } //头结点不能动需要辅助指针进行遍历 HeroNode2 temp = head.next; while (true){ if (temp == null){//找到链表的最后位置 break; } System.out.println(temp);//输出 temp = temp.next;//temp后移 } } /*添加数据不考虑顺序*/ public void add(HeroNode2 heroNode){ //找到链表的最后一个位置 HeroNode2 temp = head; while (true){ if (temp.next == null){//找到链表的最后位置 break; } //如果没有找到将指针temp后移 temp = temp.next; } //当循环结束时,指针找到链表的最后的位置 /*形成一个双向链表*/ temp.next = heroNode; heroNode.pre=temp; } /*修改节点数据根据编号进行修改*/ public void update(HeroNode2 heroNode){ if (head.next ==null){//判断链表是否为空 System.out.println("链表为空,不能修改"); return; } //进行修改 HeroNode2 temp = head.next; boolean flag = false; //添加的编号是否存在 while (true){ if (temp ==null){ break; } if (temp.no == heroNode.no){ flag = true; break; } temp = temp.next; } if (flag){ temp.name = heroNode.name; temp.nickname = heroNode.nickname; }else { System.out.printf("没有找到编号%d的节点,不能修改\n",heroNode.no); } } /*双向链表删除节点*/ //1.直接找到要删除节点 public void del(int no){ if (head.next == null){//判断链表是否为空 System.out.println("链表为空,不能删除"); return; } HeroNode2 temp=head.next;//辅助指针 boolean flag=false; while (true){ if (temp == null){ break; } if (temp.no==no){ flag=true; break; } temp=temp.next; } if (flag){ //temp.next=temp.next.next; temp.pre.next=temp.next; //当删除最后一个节点时有问题 会出现空指针异常 if (temp.next!=null){ temp.next.pre=temp.pre; } }else { System.out.printf("没有找到编号%d节点的数据",no); } } }
3.进行测试
public class DoubleLinkedListDemo { public static void main(String[] args) { //进行初始 HeroNode2 heroNode1 = new HeroNode2(1, "宋江", "及时雨"); HeroNode2 heroNode2 = new HeroNode2(2, "卢俊义", "玉麒麟"); HeroNode2 heroNode3 = new HeroNode2(3, "吴用", "智多星"); HeroNode2 heroNode4 = new HeroNode2(4, "林冲", "豹子头"); DoubleLinkedList doubleLinkedList = new DoubleLinkedList(); //加入 doubleLinkedList.add(heroNode1); doubleLinkedList.add(heroNode2); doubleLinkedList.add(heroNode3); doubleLinkedList.add(heroNode4); System.out.println("修改前------"); doubleLinkedList.list(); HeroNode2 heroNode5 = new HeroNode2(4, "林冲1", "豹子头1"); System.out.println("修改后------"); /*修改测试*/ doubleLinkedList.update(heroNode5); doubleLinkedList.list(); /*删除测试*/ doubleLinkedList.del(4); System.out.println("删除后-----"); doubleLinkedList.list(); } }