数据结构——链表

链表(Linked List)

一种线性数据结构,其中的每个元素都是一个节点对象。各个节点通过“引用”(指针)相连接,引用中记录了下一个节点的内存地址,通过其可以定位并访问到下一个节点。

链表对比数组有更好的灵活性,数组要求内存空间是连续的,但当数组非常庞大时,可能无法提供那么大的连续空间,同时数据量庞大的数组除了对其进行查找操作外,其余操作都非常耗费资源,这时链表的灵活性就体现出来了。

 如图所示,链表的结构如图。故而在相同数据量下,链表比数组要占据更多的内存空间,而链表的增删操作在时间复杂度O(1),数组为O(n)。这是典型的空间换时间的设计。

对于链表的基本操作如下(单链表介绍)

 

  1 class ListNode {
  2     int val;
  3     ListNode next;
  4     ListNode(int x){
  5         val = x;
  6     }
  7 }
  8 public class LinkList {
  9     public static void main(String[] args){
 10 
 11         Scanner in = new Scanner(System.in);
 12         //初始化链表
 13         ListNode n0 = new ListNode(1);//头节点,整个链表的代称,该链表可记作n0
 14         ListNode n1 = new ListNode(3);
 15         ListNode n2 = new ListNode(2);
 16         ListNode n3 = new ListNode(4);
 17         ListNode n4 = new ListNode(5);
 18         n0.next = n1;
 19         n1.next = n2;
 20         n2.next = n3;
 21         n3.next = n4;
 22 
 23         ListNode HeadNode = n0;
 24         //遍历链表
 25         showList(HeadNode);
 26 
 27         //插入节点
 28         HeadNode = insert(HeadNode,1,new ListNode(6));
 29 
 30         showList(HeadNode);
 31 
 32 //        HeadNode = deleteList(HeadNode,3);
 33 //        showList(HeadNode);
 34 //
 35 //        int target = in.nextInt();
 36 //        System.out.println("值为"+target+"的索引为"+findNode(HeadNode,target));
 37 
 38         alterList(HeadNode,3,7);
 39         showList(HeadNode);
 40 
 41     }
 42 
 43     //指定位置插入节点
 44     public static ListNode insert(ListNode headnode,int n,ListNode p){
 45         ListNode temp = headnode;
 46         if ( n == 1) {
 47             p.next = headnode;
 48             headnode = p;
 49             return headnode;
 50         }
 51         for (int i = 1; i < n-1; i++) {
 52             if (temp.next == null){
 53                 temp.next = p;
 54             }
 55             temp = temp.next;
 56         }
 57         p.next = temp.next;
 58         temp.next = p;
 59         return headnode;
 60     }
 61 
 62     public static void showList(ListNode headnode){
 63         for (ListNode node=headnode; node != null ; node = node.next) {
 64             if (node.next == null){
 65                 System.out.print(node.val+"\n");
 66             }else
 67                 System.out.print(node.val+"->");
 68         }
 69     }
 70 
 71     public static ListNode deleteList(ListNode headnode,int n){
 72         ListNode temp = headnode;
 73         if (n == 1) {
 74             headnode = headnode.next;
 75             return headnode;
 76         }
 77         for (int i = 1; i < n-1; i++) {
 78             temp = temp.next;
 79         }
 80         temp.next = temp.next.next;
 81         return headnode;
 82     }
 83 
 84     public static int findNode(ListNode headnode,int target){
 85         int index = 1;
 86         while(headnode!=null){
 87             if (headnode.val == target)
 88                 return index;
 89             headnode = headnode.next;
 90             index++;
 91         }
 92         return index;
 93     }
 94 
 95     public static void alterList(ListNode HeadNode,int index,int n){
 96         for (int i = 1; i < index ; i++) {
 97             HeadNode = HeadNode.next;
 98         }
 99         HeadNode.val = n;
100     }
101 }

其实对于链表的操作而言,难点在于“节点”的定位,所有函数中都使用临时指针来进行操作,可以把其当成数组的索引(下标)。

比如在插入节点操作中,其难点就在于要定位至要插入位置的前一个节点,也就是说要临时指针指向插入位置的前一个节点。

还有一个小问题我纠结了好久,就是在初学时不同资料在遍历定位时使用HeadNode或者HeadNode.next,其实这很无所谓,只要搞明白你要使用什么去操作,习惯用节点就使用HeadNode,习惯放弃头节点而专注于HeadNode.next就用HeadNode.next

对于环形链表和双向链表而言,其操作的核心思想和单链表大同小异。在环形链表中,难点除了上面提到的,便是对于“头节点”的定位,环形链表中,为了不迷路,方便操作,最好使用一个指针指定一个节点作为“头节点”,也就是要指定一个方向标!而双向链表更加简单,仅仅只是在定义节点时要加上前驱指针,同时操作时也要考虑前驱节点。

数组VS链表

posted @ 2024-02-15 16:22  abababiubiu  阅读(7)  评论(0编辑  收藏  举报