1.单链表
下一个节点的引用即是存放下个节点的地址 以便于连成一个链表
初始化属性
提供一个带有data next 的构造方法 以便于后面使用
class Node{
public int data;
public Node next;
//Node为引用类型 head存放头部地址
public Node head;
public Node(int data){
//初始data默认为data 因为data是传入的
this.data=data;
//next为一个引用 因此须默认为null 以便于后面使用
this.next=null;
}
一 头插法
把一个节点插入无头单向非循环单链表的头部 并且让head指向这个节点
public void addFirst(int data){
//1.先生成一个节点
// 把这个生成的节点赋值为data
Node node=new Node(data);
if(this.head==null){//证明第一次插入节点
this.head=node;
return ;
}
//2.把null改为插入到最前面第一个节点的地址 它存在head中
node.next=this.head;
//3.更改头部地址 跟node.next 地址不一样 自己想想
head指向新头部节点 node存的是当前节点地址
this.head=node;
}
若插入有头 插到头节点与第二个节点中间
步骤总结:
1.Node node=new Node(data);
node是new之后生成的对象
node这个对象可以通过调用属性来方便后面调用 把data赋给数值域
2.node.next=this.head;
将next改为第一个节点的地址 则它成为新头节点
3.this.head=node;
node其实就是保存了新节点的地址
应当赋值让head指向这个新头节点 head保存头节点的地址
注意:
如果是第一次插入节点 那么head为null
则须使head保存这第一个节点的地址
this.head=node; 让head指向node 指向新节点
易错点 *
不可写成
1.Node node=new Node(data);
2.this.head=node;
3.node.next=this.head;
会导致节点自己指向自己 直接报废
分析如下
1.表示new一个新对象 方便调用
2.node存放的是当前节点的地址 则第二步地址head指向这个尚未插入的新节点
3.node调用next属性并且赋值head head指向新节点自身 即存新节点自身的地址
那么next 最后赋成了自身的地址
恭喜 直接报废 重开
还有一个重要的知识记录一下
Node是一个类
它的属性有data next 相当于一个模板 根据这个模板产生无数个对象
如对象node可以调用属性
再复习一遍 可能不完整但是核心
Node node=new Node(data);
if(this.head==null){
this.head=node;
return ;
}
node.next=head;
this.head=node;
每new一次 它都可以产生一个新节点
二 打印单链表
重新定义一个cur 如果用head会导致到后面移了之后找不到了
代码如下
public void display(){
Node cur=this.head;
while(cur!=null){
System.out.println(cur.data);
cur=cur.next;
}
分析如下:
1.Node cur=this.head;
重新找一个对象为cur 并且赋予head 由于head存放头节点的地址
所以cur指向头节点
2.while(cur!=null) {
打印cur.data数据 通过cur来调用
cur=cur.next; 前面说过next中存放的都是下一个节点的地址 因此cur赋值后可指向下一个节点
当没有下一个节点时 即为cur.next==null 赋值之后cur等于null
为了能打印最后一个节点 则条件为cur!=null 若为cur.next!=0 最后一个节点data不会打印
三 尾插法
把最后一个节点的null变为要插入节点的地址 如图是777
Node cur=this.head;
让一个cur指向头节点 然后让cur往后走 当cur.next==null时证明ok了
此时把 cur.next=node; 因为node中存着尾插节点的地址
实现代码如下
最重要的一点是 通过第一步创建新的节点 记住了 跟头插的时候一模一样
Node node=new Node(data);
public void addLast(int data){
//传入一个数据data
//最重要的一点是 通过第一步创建新的节点 记住了 跟头插的时候一模一样
Node node=new Node(data);
//cur指向头节点
Node cur=this.head;
while(cur.next!=null){
cur=cur.next;
}
cur.next=node;
}
四 查找key是否在单链表中
思想与前面类似 代码如下
public boolean contains(int key){
//当然第一步可省略new一个node 直接第二步开始 但习惯了还是写上去了
Node node=new Node(data);//由于前面用构造方法实现了data 所以这里可直接用
Node cur=this.head;//同理 前面构造方法也实现了head 这里也可以直接用
while(cur!=null){
//找到了
if(cur.data==key){
return true;
}
//没找到 继续往后
cur=cur.next;
}
return false;
}
五 得到单链表的长度
public int size(){
Node cur=this.head;
int count=0;
while(cur!=null){
count++;
cur=cur.next;
}
return count;
}
this总结
this可以调用属性或方法 养成习惯就好了
this不可以访问局部变量
如 这是错误的 a不是对象 a在栈里
这是正确的 a定义在外面 this可以访问引用了
六 在index(任意位置)插入一个数据(data)
你要插到2号位 那么就插到2号位的前面即可 注意是从0号位开始的
最终的目的就是把其串起来
代码如下
// 表示要在下标为index处 插入数据为data的节点
public void addIndex(int index,int data){
Node node=new Node(data);
//当index为0时 相当于头插
if(index==0){
addFirst(data);
return ;
}
//当index等于长度时 等价于尾插
if(index==this.size()){
addLast(data);
return ;
}
//先找到index位前面一个节点的地址
Node cur=searchIndex(index);
//找到之后进行插入
node.next=cur.next;
cur.next=node;
private Node searchIndex(int index){
//先堆index检查是否合法
if(index<0||index>this.size()){
throw new RuntimeException("index位置不合法");
}
Node cur=this.head;
while(index-1!=0){
cur=cur.next;
index--;
}
return cur;
}
}
七 删除第一次出现关键字为key的节点
代码如下
private Node searchprev(int key) {
Node prev = this.head;
while (prev.next != null) {
//如果下一个数据等于key时 返回prev
if (prev.next.data == key) {
return prev;
//如果不是继续往后找
} else {
prev = prev.next;
}
}
//由于Node 为引用类型 那么返回null 代表一个节点的属性
// data next
return null;
}
public void remove(int key) {
if (this.head == null) {
return;
}
//如果要删的key就是头节点中的数据
if (this.head.data == key) {
this.head = this.head.next;
return;
}
//当要删除的不是头节点时 要删除的在后面
//1.先找到要删除元素的前驱(前一个)
//创建一个方法
Node prev = searchprev(key);
if(prev==null){
System.out.println("根本没有这个节点");
return ;
}
//通过Node引用del 确定del的位置
// del指向我们要删除的节点
Node del=prev.next;
//再一次进行
prev.next=del.next;
}
}
八 删除所有值为key的节点
假如把所有值为12的删除
搞出来prev与cur
这个方法思想是综合与提升
这个方法只遍历一次链表就可以删除所有关键字为key的元素
注意 先删头后面的节点到最后再返回过来看头
prev 在cur 前一个进行定义通过cur的移动变化 prev进行变化
public void removeAllKey(int key){
//先判断 从 头 后一个 是不是我们要删除的key
//最后判断头是不是要删的
//Node定义引用一个prev指向头 Node本身就是引用类型
Node prev=this.head;
//定义一个cur 指向头的下一个
Node cur=this.head.next;
while(cur!=null){
//当cur指向的节点中存的数据等于key时
// 我们删除
if(cur.data==key){
prev.next=cur.next;
cur=cur.next;
//注意删就删了 不可以把prev往后
// 如果往后就会可能有忽略产生 自己画图想想看
} else {
//如果不是 继续往后找
prev=cur;//把prev指向cur所指向的位置
cur=cur.next;
}
//最后看头 是否是key
if(this.head.data==key){
this.head=this.head.next;
}
}
}
}
九 释放链表
相当于诺米骨牌 推倒第一个就可以了
public void clear(){
this.head=null;
}
总代码如下
//Node 相当于一个节点 这个节点包括next和data
//Node 表示一个类型
public class Eriksen {
class Node {
public int data;
//next中存放一个地址 定义为Node类型
public Node next;
public Node(int data) {
this.data = data;
this.next = null;
}
public Node head;
public void addFirst(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
return;
}
node.next = this.head;
this.head = node;
}
public void display(int data) {
Node cur = this.head;
while (cur != null) {
System.out.println(cur.data);
cur = cur.next;
}
}
public void addLast(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
return;
}
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
public boolean contains(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.data == key) {
return true;
}
}
return false;
}
public int size() {
Node cur = this.head;
int count = 0;
while (cur != null) {
cur = cur.next;
count++;
}
return count;
}
private Node searchIndex(int index) {
//先堆index检查是否合法
if (index < 0 || index > this.size()) {
throw new RuntimeException("index位置不合法");
}
Node cur = this.head;
while (index - 1 != 0) {
cur = cur.next;
index--;
}
return cur;
}
// 表示要在下标为index处 插入数据为data的节点
public void addIndex(int index, int data) {
Node node = new Node(data);
//当index为0时 相当于头插
if (index == 0) {
addFirst(data);
return;
}
//当index等于长度时 等价于尾插
if (index == this.size()) {
addLast(data);
return;
}
//先找到index位前面一个节点的地址
Node cur = searchIndex(index);
//找到之后进行插入
node.next = cur.next;
cur.next = node;
}
private Node searchprev(int key) {
Node prev = this.head;
while (prev.next != null) {
//如果下一个数据等于key时 返回prev
if (prev.next.data == key) {
return prev;
//如果不是继续往后找
} else {
prev = prev.next;
}
}
//由于Node 为引用类型 那么返回null 代表一个节点的属性
// data next
return null;
}
public void remove(int key) {
if (this.head == null) {
return;
}
//如果要删的key就是头节点中的数据
if (this.head.data == key) {
this.head = this.head.next;
return;
}
//当要删除的不是头节点时 要删除的在后面
//1.先找到要删除元素的前驱(前一个)
//创建一个方法
Node prev = searchprev(key);
if(prev==null){
System.out.println("根本没有这个节点");
return ;
}
//通过Node引用del 确定del的位置
// del指向我们要删除的节点
Node del=prev.next;
//再一次进行
prev.next=del.next;
}
public void removeAllKey(int key){
//先判断 从 头 后一个 是不是我们要删除的key
//最后判断头是不是要删的
//Node定义引用一个prev指向头 Node本身就是引用类型
Node prev=this.head;
//定义一个cur 指向头的下一个
Node cur=this.head.next;
while(cur!=null){
//当cur指向的节点中存的数据等于key时
// 我们删除
if(cur.data==key){
prev.next=cur.next;
cur=cur.next;
//注意删就删了 不可以把prev往后
// 如果往后就会可能有忽略产生 自己画图想想看
} else {
//如果不是 继续往后找
prev=cur;//把prev指向cur所指向的位置
cur=cur.next;
}
//最后看头 是否是key
if(this.head.data==key){
this.head=this.head.next;
}
}
}
}
public static void main(String[] args) {
Eriksen nod=new Eriksen();
}
}