数据结构----------单链表
2018-06-18 00:41:44来源:未知 阅读 ()
数据结构(一) 单链表的实现
一. 单链表的简单介绍
单链表在整个数据结构中可以说是相对比较简单的,但是它的作用却不可忽视,许多相对复杂的数据结构都是基于单链表实现的。在这里我们简单介绍一下单链表。
单链表有节点构成,每个节点封装了数据和下一个节点的地址,最后一个节点指向null。
二. 单链表的java实现
(一)API
该实现单链表可以适合用于存储实现了Comparable接口的任意对象,因此可以对不同节点进行比较,链表内部维护了一个first 和last指针,分别指向链表的头结点和尾节点,size变量存储的是链表的长度。
内部为了了一个Node类,它的实例用于表示节点对象,存储相应的数据和下一个节点的地址。
该实现可以用来添加节点、删除节点、遍历节点、反转链表等,具体介绍参见具体实现。
(二)具体实现
- 添加节点
1. 插入节点(默认从尾部插入)
/** * 向链表中添加节点(从链表尾部添加节点) * 如果链表为空,则链表的第一个节点的最后一个节点都指向新添加的节点 * 如果链表不为空,将最后一个节点的next指向新产生的节点,并将新节点设为最后一个节点 * 节点数加1 * @param data */ public void add(T data) { Node newNode = new Node(data); if(first == null) {//链表为空 first = newNode; last = first; }else {//链表不为空 last.next = newNode; last = newNode; } size++;//节点数加1 }
2. 从链表头部插入节点
/** * 从链表头部添加元素 * @param data */ public void addAtHeader(T data) { Node newNode = new Node(data); if(first == null) { first = newNode; last = first; }else { newNode.next = first; first = newNode; } size++; }
- 删除节点
1. 删除头部节点
/** * 删除头结点 * @return */ public T deleteFirst() { T data = first.data; first = first.next; size--; return data; }
2. 删除指定位置节点
/** * 删除指定位置的节点 * @param index * @return */ public T deleteNode(int index) { Node preCurrent = null; Node current = first; if(index == 0) { return deleteFirst(); } int i = 0; while(i < index) { preCurrent = current; current = current.next; i++; } preCurrent.next = current.next; size--; return current.data; }
- 遍历节点
1. 遍历节点
/** * 遍历打印所有节点 */ public void print(Node node) { Node current = node; int i = 0; while(current != null) { System.out.println("This is the "+i+"th Node:"+current.data); current = current.next; i++; } }
2. Iterable实现节点遍历
/** * 遍历方法2: 链表实现Iterable接口,返回一个Iterator对象 * */ @Override public Iterator<T> iterator() { return new Iterator<T>() { Node current = first;//内部维护一个当前节点对象 @Override public boolean hasNext() { return current != null; } @Override public T next() { Node returnNode = current; current = current.next;//小心 return returnNode.data; } }; }
- 反向遍历节点
/** * * 从为到头遍历链表:递归实现 * 也可采用栈,正向遍历压栈,循环完毕,弹栈输出 */ public void printFromLastToFirst() { reversePrint(first); } public void reversePrint(Node n) { if(n == null) { return; } reversePrint(n.next); System.out.println("Node: "+n.data); }
- 链表反转
/** * 链表反转 * 注:也可采用递归实现 * @return:返回最后反转后链表的第一个节点 */ public Node reverseNode() { //注:当前节点指的是即将要添加到反转后链表的节点 Node reverseHeader = null;//反转后链表的第一个节点 Node current = first;//当前节点 Node next = null;//原链表中当前节点的下一个节点 Node before = null;//反转后链表的当前节点的 前一个节点 while(current != null) { next = current.next;//保存当前节点的下一个节点 if(reverseHeader == null) {//反转链表中的第一个节点 reverseHeader = current; reverseHeader.next = null; }else {//反转链表的第一个以后的节点 before = reverseHeader; reverseHeader = current; reverseHeader.next = before; } current = next;//将当前节点指向下一个节点 } return reverseHeader;//返回反转后链表的头结点 }
- 返回链表第k个元素
1. 实现1
/** * 返回链表倒数第k个节点(实现1) * 1. 返回链表总长度,计算倒数第k个节点的索引 * 2. 遍历并返回倒数第k个节点 * @param index * @return */ public T findLastNode1(int index) { Node returnNode = first; int k = size()-index;//k为要返回节点的索引 if(k < 0) { throw new NullPointerException("链表长度越界 "); } for(int i = 0; i < k; i++) { returnNode = returnNode.next; } return returnNode.data; }
2. 实现2
/** * 返回链表倒数第k个元素(实现2) * 倒数第k个元素和最后一个元素相差k-1,所以可以选择两个指针 * 1. 将第一个指针从头结点移动k-1次,使头结点和这个节点相差k-1 * 2. 同时移动两个节点,当该节点是最后一个节点时,头结点为倒数第k个节点 * @param index * @return */ public T findLastNode2(int index) { Node firstNode = first; Node lastNode = first; //lastNode向后移动k-1次 for(int i = 0; i < index - 1; i++) { if(lastNode == null) { throw new NullPointerException("链表长度越界"); } lastNode = lastNode.next; } //同时移动,指针lastNode为最后一个节点 while(lastNode.next != null) { firstNode = firstNode.next; lastNode = lastNode.next; } return firstNode.data; }
- 合并两个有序链表
/** * 合并两个有序链表 * @param head2 要合并的链表的头结点 * @return 合并后的有序链表的头结点 */ public Node merge(Node head2) { Node head1 = this.first;//当前链表的头结点 if(head1 == null && head1 == null) return null; if(head1 == null) return head2; if(head2 == null) return head1; Node head = null;//新生成链表的头结点 Node current = null;//新生成链表的当前节点 while(head1 != null || head2 != null) {//至少一个链表没有循环完 //1.为新生成的链表添加第一个节点 if(head == null) { if(head1.data.compareTo(head2.data) < 0) { head = head1; current = head; head1 = head1.next; }else { head = head2; current = head; head2 = head2.next; } } //2.为生成的节点添加后续节点 //如果head1为空,直接将当前节点指向head2,返回head即可 if(head1 == null) { current.next = head2; return head; } //如果head2为空,直接将当前节点指向head1,返回即可 if(head2 == null) { current.next = head1; return head; } //如果两个节点都不为空,进行比较,比较后继续循环 if(head1.data.compareTo(head2.data) < 0) { current.next = head1; current = head1; head1 = head1.next; }else { current.next = head2; current = head2; head2 = head2.next; } } return head; }
- 判断链表是否有环
/** * 判断单链表是否有环 * 使用两个指针,first指针每次移动一步,second指针每次移动2步 * @return */ public boolean hasCycle() { Node first = this.first; Node second = this.first; while(second != null) { first = first.next;//每次移动一次 second = second.next.next;//每次移动两次 if(first == second) { return true; } } return false; }
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 数据结构:用实例分析ArrayList与LinkedList的读写性能 2020-06-04
- 合并有序两个单链表,合并后链表依然有序 2020-06-02
- 数据结构之链式队列的代码实现及有趣应用 2020-05-29
- 学好程序员必知必会的数据结构,这一份书单你值得拥有! 2020-05-12
- 【数据结构】数组 2020-05-08
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash