目录
设计链表
描述
示例
提示
单链表:
双链表:
设计链表的实现。您可以选择使用单链表或双链表。
单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。
假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
示例
MyLinkedList linkedList = new MyLinkedList(); linkedList.addAtHead(1); linkedList.addAtTail(3); linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3 linkedList.get(1); //返回2 linkedList.deleteAtIndex(1); //现在链表是1-> 3 linkedList.get(1); //返回3
单链表的数据结构比较简单,只有值和指向下一个节点的指针。
在设计单链表的时候,为了让头节点的删除和插入和其他节点操作相同,我们引入了一个虚拟头节点,真正的数据节点从第二个节点开始。
class MyLinkedList { private MyListNode head;//虚拟头节点,该节点指向第一个数据节点 private int len;//存储链表的长度信息 public MyLinkedList() { head = new MyListNode(); len = 0; } public int get(int index) { if (index<0||index>=len) return -1; MyListNode cur=head.next;//从第一个数据节点开始 for (int i = 0; i < index; i++) { cur=cur.next; } return cur.val; } public void addAtHead(int val) { addAtIndex(0,val); } public void addAtTail(int val) { addAtIndex(len,val); } public void addAtIndex(int index, int val) { if (index>len) return; if (index<0) index=0; MyListNode pre=head; for (int i = 0; i < index; i++) { pre=pre.next; } MyListNode node=new MyListNode(val);//创建新节点 node.next=pre.next;//将当前节点插入到位置中 pre.next=node; len++;//链表总长度+1 } public void deleteAtIndex(int index) { if (index<0||index>=len) return;//越界直接退出 MyListNode pre=head; for (int i = 0; i < index; i++) { pre=pre.next; } pre.next=pre.next.next;//删除节点 len--;//链表总长度-1 } } class MyListNode { public int val; public MyListNode next; public MyListNode() { val = 0; next = null; } public MyListNode(int val) { this.val = val; } }
双链表比单链表多了一个指针,指向前面一个节点,这会让删除和插入操作变得简便一些。
class MyLinkedList { private MyListNode head; private MyListNode tail; private int len; public MyLinkedList() { head=new MyListNode(0); tail=new MyListNode(0); head.next=tail; tail.pre=head; len=0; } public int get(int index) { if (index<0 ||index>=len) return -1;//越界直接返回-1 MyListNode cur=head; if (index+1<len-index){//若元素在靠近头节点的地方,则从头开始遍历 for (int i = 0; i < index+1; i++) cur=cur.next; }else{//若元素在靠近尾节点的地方,则从尾部开始遍历 cur=tail; for (int i = 0; i < len-index; i++) cur=cur.pre; } return cur.val; } public void addAtHead(int val) { MyListNode pre=head,first=head.next; //添加数据到头部 MyListNode node=new MyListNode(val); node.pre=pre;node.next=first; pre.next=node;first.pre=node; len++; } public void addAtTail(int val) { MyListNode last=tail,pre=tail.pre; //添加数据到尾部 MyListNode node=new MyListNode(val); node.pre=pre;node.next=last; pre.next=node;last.pre=node; len++; } public void addAtIndex(int index, int val) { if (index>len) return; if (index<0) index=0; MyListNode pre,cur; if (index+1<len-index){//若元素在靠近头节点的地方,则从头开始遍历 pre=head; for (int i = 0; i < index; i++) pre=pre.next;//遍历到需要插入的前一个位置 cur=pre.next;//需要插入的节点位置 }else{//若元素在靠近尾节点的地方,则从尾部开始遍历 cur=tail;//需要插入的节点位置 for (int i = 0; i < len-index; i++) cur=cur.pre; pre=cur.pre;//需要插入的前一个位置 } MyListNode node=new MyListNode(val); node.pre=pre;node.next=cur;//将新节点插入到pre和cur之间 pre.next=node;cur.pre=node; len++; } public void deleteAtIndex(int index) { if (index<0 || index>=len) return;//越界直接返回 MyListNode pre,cur; if (index+1<len-index){//若元素在靠近头节点的地方,则从头开始遍历 pre=head; for (int i = 0; i < index; i++) pre=pre.next;//遍历到需要删除的前一个位置 cur=pre.next.next;//删除节点的后一个节点 }else{//若元素在靠近尾节点的地方,则从尾部开始遍历 cur=tail;//需要删除的后一个节点 for (int i = 0; i < len-index-1; i++) cur=cur.pre; pre=cur.pre.pre;//需要删除的前一个位置 } pre.next=cur;//删除当前节点 cur.pre=pre; len--; } } class MyListNode{ public int val;//节点存储的值 public MyListNode pre;//指向当前节点的前一个节点 public MyListNode next;//指向当前节点的后一个节点 public MyListNode(int val){ this.val=val; } }