1単一リンクリストの説明
(1)単一リンクリストの構成
単一リンクリスト:単一リンクリストは、さまざまなノード(ノード)で構成される線形テーブルです。メモリに格納されたデータは連続的ではありません。格納されたデータはメモリに分散されます。各ノードは、それだけが知ることができます。次のノードの保存場所。単独でリンクされたリストは、N個のノード(ノード)で構成され、各ノードは、このノードのデータ(データ)と記録されているのアドレス(ポインタ、参照)、次のノード。外部に公開されているヘッドノード(Head)は1つだけです。リンクリストのすべての操作は、そのヘッドノードを介して直接または間接的に実行され、最後のノードポインタフィールドはNULLを指します。
上記の情報から、リンクリストは次の要素で構成されていることがわかります。
- 構成:データフィールド+ポインタフィールド
- データフィールドには特定のデータが格納され、ポインタフィールドには次のノードを指すアドレスが格納されます。簡単に言えば、次のノードのアドレスが格納されます。アドレス情報ではなく、次のノードの位置情報がここに格納されることに注意してください。データフィールドの。
- Javaでは、ノードはオブジェクトであり、オブジェクトにはデータ属性と参照属性が含まれ、参照は次のオブジェクト(ノード)を指していると簡単に理解できます。Javaで参照型の変数を定義するときは、実際にはオブジェクトが配置されているアドレスを定義します。
- 単一リンクリストのテールノードがNULLを指している
Java参照とポインターの違いは、次のように参照できます。
引用才是真正含义上的地址,指针不是地址是内存地址,两者关注对象不一样,引用关注的是对象本身,地址随对
象的改变而改变,因为在创建对象的时候,既存数据本身,也将该对象的地址保存其来,和普通变量的地址不一
样,普通变量的地址就指的是内存地址,C中的指针关注的对象就是内存地址,他不关心该地址存储的是什么,只
关系该地址本身,如果该地址内存空间中东西搬走了,指针不会自动改变指向,java中对象重建了地址也变了)
具体的な定義は、次の図を参照しています。
次の図に示すように、Javaはオブジェクトの観点から理解されます。
2単一リンクリストの実現
3単一リンクリストの逆転
(1)反復法の実装
最初に回路図を見1->2->3->4->5
て、例としてリンクリストを取り上げます。
リンクリストの問題解決のアイデア:
(1)既知の情報ヘッドノード
(2)先行ノードと後続ノードを定義します
(3)補助ノードを構築する
(4)仮想ヘッドノードを構築します(ヘッドノードの処理に関するいくつかの問題を回避できます)
package jttl.jxresearch.com.hive.udf.test;
import java.util.HashSet;
import java.util.TreeSet;
public class MyLink {
// // Node head = null;
// class Node {
//
// Node next = null;
// int data;
//
// public Node(int data){
//
// this.data = data;
//
// }
// }
public static void main(String[] args) {
MyLink list = new MyLink();
Node node = new Node(1);
list.insert(1,node);
list.insert(2,node);
list.insert(6,node);
list.insert(3,node);
list.insert(4,node);
list.insert(5,node);
list.insert(6,node);
list.display(node);
Node head = list.removeElements(node, 6);
//LinkNode head = list.removeElementsByStack(list.head, 6);
//LinkNode head = list.removeRepeat(list.head);
list.display(head);
}
/**
* 向链表中插入数据
*
* @param data
*/
public void addNode(int data,Node head){
Node node = new Node(data);
if (head == null) {
head = node;
return;
}
//构建辅助节点
Node cur = head;
while(cur.next != null){
cur = cur.next;
}
//遍历结束时,此时tmp节点地址指向为null说明是最后一个节点,只需要将tmp.next赋值为添加节点地址即可
cur.next = node;
}
public void insert(int val,Node head){
if(head==null){
head = new Node(val);
}else{
Node cur = head;
while(cur.next!=null){
cur = cur.next;
}
cur.next = new Node(val);
}
}
public void display(Node head){
System.out.print("list:");
Node cur = head;
while(cur!=null){
System.out.print(cur.data+"->");
cur = cur.next;
}
System.out.print("null");
System.out.println();
}
/**
*
* @return 返回节点长度
*/
public int length(Node head){
int length = 0 ;
Node tmp = head;
while(tmp != null){
length ++;
tmp = tmp.next;
}
return length;
}
/**
* 删除链表中指定值的节点
*
* @param head,dataue
* @return
*/
public Node removeElements(Node head,int data){
//如果是头节点需要删除
while(head != null){
if(head.data != data){
break;
}
head = head.next;
}
//如果不是头节点需要删除
Node pre = head;
Node cur = head.next;
while (cur != null){
if (cur.data != data){
pre = cur;
}
pre.next = cur.next;
cur = cur.next;
}
return head;
}
//这道题还有一个变种,就是删除重复的节点,不是将重复的所有节点删除,而是保留第一个节点,后面如果重复,就删除。这里也给出代码:
//先给出一道其他算法去重的题目理解一下
/**
* 把字符串去重,并升序排序
* @param str
* @return
*/
public static String sort2(String str) {
//把String变成单一字符数组(获取字符数组)
String[] chars = str.split("");
//把字符串数组放入TreeSet中,根据set元素不重复的特性去掉重复元素。根据treeSet的有序性排序
TreeSet<String> treeSet = new TreeSet();
for (String s : chars) {
treeSet.add(s);
}
//把treeSet拼接成字符串
str = "";
for (String s : treeSet) {
str += s;
}
return str;
}
public Node removeRepeat(Node head){
if(head==null){
return null;
}
HashSet<Integer> set = new HashSet<Integer>();
set.add(head.data);
//定义前驱节点
Node pre = head;
//定义当前节点
Node cur = head.next;
while(cur!=null){
//如果当前节点不为null则判断当前节点的数据是否在集合中,存在就删除,不存在就继续遍历
if(set.contains(cur.data)){
//删除当前节点
pre.next = cur.next;
}else{
set.add(cur.data);
pre = cur;
}
cur = cur.next;
}
return head;
}
/**
* 单链表的反转
*
* @param head,dataue
* @return
*/
public Node reverseLink(Node head){
//定义前驱节点,初始化为NULL。链表反转后头节点会指向NULL所以将前驱节点定义为NULL
Node pre = null;
//定义当前节点
Node cur = head;
//当当前节点不为NULL的时候,进行遍历
while(cur != null){
//定义下一节点。将当前节点的的指向信息保存起来,因为在下一步,当前节点的指向信息被覆盖了
Node tmp = cur.next;
//将当前节点的中指向下一个节点的地址修改为前驱节点
cur.next = pre;
//在遍历的过程中,游标(指针)移动。每遍历一次,前驱节点向前移动一次,当前节点向前移动一次。
//也就是将当前节点的地址给前驱节点,当前节点移动到下一个节点的位置,如此循环
pre = cur;
cur = tmp;
}
//此时pre是头节点
return pre;
}
/**
* 单链表的反转:反转从位置m到n的链表(1=<m<=n<=链表长度)。虚拟头节点
*
* @param head,dataue
* @return
*
* 解析:本题,还有下一个要介绍的困难级别的题目,算是一种类型题,它们都是要对链表中特定区间中的节点进行操作。面对这种题目,有固定的套路可以帮你简化解题思路,套路如下:
*
* 给链表添加虚拟头节点 dummy,这样就不需要再单独考虑头节点了,可以省去很多麻烦;
* 找到需要操作的链表区间,区间起始节点用 start 表示,结束节点用 end 表示;
* 对区间上的链表进行操作;
* 将操作后的链表重新接回原链表,这里我们需要另外两个变量,前驱节点 prev 和后继节点 successor。
*/
}
リンクリストは、仮想ヘッドノードを介して問題を処理します
次の図に示すように、リンクリストの仮想ヘッドノードを構築します。
ダミーヘッドノードをNULLとして構築し、dummyHeadと呼びます
dumpyHeadノードを構築した後、実際には、先行ノードがheadではなくNULLを指すようにします。
public class LinkedList<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
}
public class LinkedList<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node head;
private int size;
public LinkedList(){
head = null;
size = 0;
}
// 获取链表中的元素个数
public int getSize(){
return size;
}
// 返回链表是否为空
public boolean isEmpty(){
return size == 0;
}
// 在链表头添加新的元素e
public void addFirst(E e){
// Node node = new Node(e);
// node.next = head;
// head = node;
head = new Node(e, head);
size ++;
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
public void add(int index, E e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Illegal index.");
if(index == 0)
addFirst(e);
else{
Node prev = head;
for(int i = 0 ; i < index - 1 ; i ++){
prev = prev.next;
}
// Node node = new Node(e);
// node.next = prev.next;
// prev.next = node;
//优雅的写法
prev.next = new Node(e, prev.next);
size ++;
}
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
//优雅的写法,通过虚拟头结点,避免处理头节点的问题
public void add(int index, E e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Illegal index.");
Node dummyHead = new Node(null,null);
Node prev = dummyHead;
for(int i = 0 ; i < index ; i ++)
prev = prev.next;
prev.next = new Node(e, prev.next);
size ++;
}
// 在链表末尾添加新的元素e
public void addLast(E e){
add(size, e);
}
}
public class LinkedList<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList(){
dummyHead = new Node();
size = 0;
}
// 获取链表中的元素个数
public int getSize(){
return size;
}
// 返回链表是否为空
public boolean isEmpty(){
return size == 0;
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
public void add(int index, E e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Illegal index.");
Node prev = dummyHead;
for(int i = 0 ; i < index ; i ++)
prev = prev.next;
prev.next = new Node(e, prev.next);
size ++;
}
// 在链表头添加新的元素e
public void addFirst(E e){
add(0, e);
}
// 在链表末尾添加新的元素e
public void addLast(E e){
add(size, e);
}
// 获得链表的第index(0-based)个位置的元素
// 在链表中不是一个常用的操作,练习用:)
public E get(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Get failed. Illegal index.");
Node cur = dummyHead.next;
for(int i = 0 ; i < index ; i ++)
cur = cur.next;
return cur.e;
}
// 获得链表的第一个元素
public E getFirst(){
return get(0);
}
// 获得链表的最后一个元素
public E getLast(){
return get(size - 1);
}
// 修改链表的第index(0-based)个位置的元素为e
// 在链表中不是一个常用的操作,练习用:)
public void set(int index, E e){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Set failed. Illegal index.");
Node cur = dummyHead.next;
for(int i = 0 ; i < index ; i ++)
cur = cur.next;
cur.e = e;
}
// 查找链表中是否有元素e
public boolean contains(E e){
Node cur = dummyHead.next;
while(cur != null){
if(cur.e.equals(e))
return true;
cur = cur.next;
}
return false;
}
// 从链表中删除index(0-based)位置的元素, 返回删除的元素
// 在链表中不是一个常用的操作,练习用:)
public E remove(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");
Node prev = dummyHead;
for(int i = 0 ; i < index ; i ++)
prev = prev.next;
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size --;
return retNode.e;
}
// 从链表中删除第一个元素, 返回删除的元素
public E removeFirst(){
return remove(0);
}
// 从链表中删除最后一个元素, 返回删除的元素
public E removeLast(){
return remove(size - 1);
}
// 从链表中删除元素e
public void removeElement(E e){
Node prev = dummyHead;
while(prev.next != null){
if(prev.next.e.equals(e))
break;
prev = prev.next;
}
if(prev.next != null){
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
}
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
Node cur = dummyHead.next;
while(cur != null){
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
リートコード演習の詳細な説明
public class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
//链表节点的构造函数
//使用arr为参数,创建一个链表,当前的ListNode为链表的头节点
public ListNode(int[] arr){
if(arr.length == 0 || arr == null){
throw new IllegalArgumentException("arr can not be empty");
}
this.val = arr[0];
ListNode cur = this;
for (int i = 1; i <arr.length ; i++) {
cur.next = new ListNode(arr[i]);
cur=cur.next;
}
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
ListNode cur = this;
while(cur != null){
res.append(cur.val + "->");
cur=cur.next;
}
res.append("NULL");
return res.toString();
}
}
public class testLeetcode {
//虚拟头节点解法
public ListNode removeElements(ListNode head,int val){
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode prev=dummyHead;
ListNode cur=prev.next;
while(cur!=null){
if(cur.val == val){
prev.next=cur.next;
}
//不能写在else语句块中,否则while条件执行不了,while条件更新的状态一定在while语句块中
prev = cur;
cur=cur.next;
}
//返回头节点
return dummyHead.next;
}
public ListNode removeElements2(ListNode head,int val){
//如果是头节点需要删除
while(head != null){
if(head.val != val){
break;
}
head = head.next;
}
//如果不是头节点需要删除
ListNode pre = head;
ListNode cur = head.next;
while (cur != null){
if (cur.val == val){
pre.next = cur.next;
}
pre = cur;
cur = cur.next;
}
return head;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 6, 5, 6};
ListNode head = new ListNode(nums);
System.out.println(head);
ListNode res = (new testLeetcode()).removeElements(head, 1);
System.out.println(res);
}
}