双链表算法

  • 双链表最大的优势就是可以双向遍历
package main

import "fmt"

type Node struct {
	num  int
	name string
	pre  *Node
	next *Node
}

// 末尾插入
func insert(head *Node, newNode *Node) {
	tmp := head
	for {
		//  插入末尾,先连接左边,后连接右边
		if tmp.next == nil {
			tmp.next = newNode
			newNode.pre = tmp
			break
		}
		tmp = tmp.next
	}
}

// 正向遍历
func list(head *Node) {
	tmp := head
	if tmp.next == nil {
		fmt.Printf("link is null")
	}

	for {
		if tmp.next != nil {
			// 第一个tmp代表head的下一个
			fmt.Printf("[%d] = %s \n", tmp.next.num, tmp.next.name)
		} else {
			break
		}
		tmp = tmp.next
	}
}

// 反向遍历
func reverselist(head *Node) {
	tmp := head
	if tmp.next == nil {
		fmt.Printf("link is null")
	}
	// 先将tmp指向链表末尾
	for {
		if tmp.next == nil {
			break
		}
		tmp = tmp.next
	}

	for {
		// 注意头节点并不是空的,里面还有mext和pre
		if tmp.pre != nil {
			fmt.Printf("[%d] = %s \n", tmp.num, tmp.name)
		} else {
			break
		}
		tmp = tmp.pre
	}
}

func main() {
	// head是随机位置的指针
	head := &Node{}

	// 插入数据不需传递下一个node地址
	node := &Node{
		num:  1,
		name: "amber",
	}
	node2 := &Node{
		num:  2,
		name: "alice",
	}
	node3 := &Node{
		num:  3,
		name: "necy",
	}
	node4 := &Node{
		num:  4,
		name: "bella",
	}

	insert(head, node)
	insert(head, node2)
	insert(head, node3)
	insert(head, node4)

	list(head)
	fmt.Println()
	reverselist(head)

}

输出结果:
在这里插入图片描述

  • 双链表有序插入和删除
package main

import "fmt"

type Node struct {
	num  int
	name string
	pre  *Node
	next *Node
}

func orderInsert(head *Node, newNode *Node) {
	tmp := head
	for {
		// 必须放在前面,防止在末尾nil,被插在最后
		if newNode.num == tmp.num {
			fmt.Printf("node has already in link\n")
			break
		}
		// 放在第二顺位,当未找到插入最后
		if tmp.next == nil {
			tmp.next = newNode
			newNode.pre = tmp
			break
		}
		// 遍历是从前往后,判断条件tmp.next是保证在tmp后面插入前插入
		if newNode.num < tmp.next.num {
			// 先关联右边,再关联左边
			newNode.next = tmp.next
			newNode.pre = tmp

			tmp.next.pre = newNode
			tmp.next = newNode
			break
		}

		tmp = tmp.next
	}

}

func list(head *Node) {
	tmp := head
	if tmp.next == nil {
		fmt.Printf("link is null")
	}

	for {
		if tmp.next != nil {
			// 第一个tmp代表head的下一个
			fmt.Printf("[%d] = %s \n", tmp.next.num, tmp.next.name)
		} else {
			break
		}
		tmp = tmp.next
	}
}

func delete(head *Node, num int) {
	tmp := head
	for {
		if tmp == nil {
			fmt.Printf("link is null")
			break
		}
		// 注意这里要找到tmp.next,因为找到tmp是要删除节点无法删除
		if tmp.num == num {
			if tmp.next == nil {
				tmp.pre.next = nil
				break
			} else {
				tmp.next.pre = tmp.pre
				tmp.pre.next = tmp.next
				break
			}
		}
		tmp = tmp.next

	}
}

func main() {
	// head是随机位置的指针
	head := &Node{}

	// 插入数据不需传递下一个node地址
	node := &Node{
		num:  1,
		name: "amber",
	}
	node2 := &Node{
		num:  2,
		name: "alice",
	}
	node3 := &Node{
		num:  3,
		name: "necy",
	}
	node4 := &Node{
		num:  3,
		name: "zoe",
	}

	orderInsert(head, node4)
	orderInsert(head, node2)
	orderInsert(head, node)
	orderInsert(head, node3)
	list(head)

	fmt.Printf("****************\n")

	delete(head, 2)
	list(head)

}


输出结果:
在这里插入图片描述
PS:这里演示的是重复插入在最后的情况,所以要将重复判断放在末尾插入之前

总结:
1、无论单链表还是双链表插入一定定位在tmp和tmp.next之间
2、头节点数据是空的,但不代表头节点=nil
3、删除和插入是一定要考虑删除的恰好是最后的节点或者插入到最后的情况

package 单双向链表; /** * 单向链表增删改查操作 * */ public class LinkTest { public static void main(String[] args) { Link l=new Link(); l.addNode("A"); l.addNode("B"); l.addNode("C"); l.addNode("D"); l.addNode("E"); l.printNode(); System.out.println("\n是否包含D:"+l.contains("D")); System.out.println("==========删除之前的内容=========="); l.printNode(); System.out.println("\n==========删除之后的内容=========="); l.deleteNode("A"); l.printNode(); } } class Link{//链表的完成类 class Node{//保存每个节点 private String data;//节点内容 private Node next;//下一个节点 public Node(String data){ this.data=data; } public void add(Node newNode) {//将节点加入到合适的位置 if(this.next==null){ this.next=newNode; }else{ this.next.add(newNode); } } public void print() {//输出节点的内容 System.out.print(this.data+"\t"); if(this.next!=null){ this.next.print();//递归调用输出 } } public boolean search(String data){//内部搜索的方 if(data.equals(this.data)){ return true; }else{ if(this.next!=null){//向下继续判断 return this.next.search(data); }else{ return false; } } } public void delete(Node previous, String data) { if(data.equals(this.data)){//找到了匹配的节点 previous.next=this.next;//空出当前的节点 }else{ if(this.next!=null){ this.next.delete(this, data);//继续查找 } } } } private Node root;//链表中的根节点 public void addNode(String data){//增加节点 Node newNode=new Node(data); if(root==null){ root=newNode; }else{ root.add(newNode); } } public void printNode(){//链表的输出 if(root!=null){ root.print(); } } public boolean contains(String name){//判断元素是否存在 return this.root.search(name); } public void deleteNode(String data){//链表删除节点 if(this.contains(data)){ if(this.root.data.equals(data)){//如果是根节点 this.root=this.root.next;//修改根节点 }else{ this.root.next.delete(root,data);//把下一个节点的前节点和要删除的节点内容一起传入 } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值