java实现单链表,以及单链表相关面试题

一、数据结构和算法的重要性

目前程序员的门槛越来越高,对于已经参加工作的程序员来说,面试或者笔试被问到数据结构和算法已经屡见不鲜,尤其是一些一线的IT公司,数据结构和算法的面试题肯定会有,要知道“算法是程序的灵魂”。作为一名程序员,如果你不想一辈子停留在“码农”这样的标签里,那么就请花些时间去提升自己的数据结构和算法方面的知识吧。

 


二、数据结构

数据结构分为线性结构和非线性结构。

线性结构作为最常用的数据结构,其特点是数据元素中存在一对一的线性关系,线性结构有两种不同的存储结构,即顺序存储结构链式存储结构。

顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的;链式存储的线性表叫做链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。常见的线性结构有:数组、队列、链表和栈

非线性结构包括:二维数组,多维数组,广义表,树结构,图结构(这里暂时不过多的进行介绍)

 


三、链表结构

 

下图是链表在内存中的存储示意图

 

1)链表以节点方式进行链式存储

2)每个节点都含有data域和next域。data域存储数据,next域指向下个数据

3)链表分为带头节点和不带头节点的链表。(上图表示带头结点)

 


四、单链表实现

下面话不多说,我们用java代码实现一个单链表,并进行增删改查的操作

1)首先定义节点(Node)

class Node {
    // 元素下标
    int index;
    // data域,存储数据
    Object data;
    // 指向下一个节点
    Node next;
​
    public Node(int index, Object data) {
        this.index = index;
        this.data = data;
    }
    @Override
    public String toString() {
        return "Node{" +
                "index=" + index +
                ", data=" + data +
                '}';
    }
}

 

2)定义头节点,实现增删改查的方法

public class SignleLinkedList {
    // 创建一个头节点
    private static final Node HEADNODE = new Node(-1, null);
​
    private Node getHead() {
        return HEADNODE;
    }
​
    // 添加
    public void add(Object data) {
        //将temp当做一个指针,一开始指向链表的头部
        Node temp = getHead();
        while (true) {
            //当前所指对象的下一个是否为空,为空就代表,此指针已经指到了链表的最后一个元素了
            if (temp.next == null) {
                break;
            }
            //将temp指针后移,一定要移动,否则就是死循环
            temp = temp.next;
        }
        // 将要添加的数据封装成节点
        Node node = new Node(temp.index + 1, data);
        // 进行数据插入
        node.next = temp.next;
        temp.next = node;
    }
​
    // 修改
    public void set(int index, Object data) {
        if (HEADNODE.next == null) {
            System.out.println("该链表为空~");
            return;
        }
        Node temp = getHead();
        // falg标记是否找到了要修改的元素
        boolean flag = false;
        while (true){
            //temp为null的时候表示链表已经到了尾部
            if (temp == null) {
                break;
            }
            if (temp.index == index){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            //修改元素属性
            temp.data = data;
        } else {
            System.out.println("没有找到该人物~");
        }
​
    }
​
    public void remove(int index) {
        //temp为指针作用
        Node temp = HEADNODE;
        //flag用来标记是否找到需要删除的元素
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                break;
            }
            if (temp.next.index == index) {
                //找到对应的元素之后,用flag标记一下
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            // 找到之后将要删除的元素的上一个节点的next直接指向删除元素的next
            temp.next = temp.next.next;
        } else {
            System.out.println("没有找到该元素");
        }
    }
​
    public void findAll() {
        if (HEADNODE.next == null) {
            System.out.println("链表为空~");
            return;
        }
        StringBuffer datas = new StringBuffer(64);
        datas.append("SignelLinkedList{");
        Node temp = getHead().next;
        // 遍历出链表里面所有的内容
        while (true) {
            if (temp.next == null) {
                datas.append(temp.data);
                break;
            }
            datas.append(temp.data);
            datas.append(", ");
            temp = temp.next;
        }
        datas.append("}");
        System.out.println(datas.toString());
    }
}

 

3)编写测试类,对链表进行测试

public class BaseTest {
    public static void main(String[] args) {
        SignleLinkedList signleLinkedList = new SignleLinkedList();
        signleLinkedList.add("张三");
        signleLinkedList.add("李四");
        signleLinkedList.add("王五");
        System.out.println("======插入三条记录结果如下:");
        signleLinkedList.findAll();
        signleLinkedList.set(2, "赵六");
        System.out.println("======将下标为2的记录改为【赵六】输出结果如下:");
        signleLinkedList.findAll();
        signleLinkedList.remove(1);
        System.out.println("======删除下标为1的记录,结果如下:");
        signleLinkedList.findAll();
    }
}

执行结果如下图所示:

至此,最简单的单链表已经实现

五、单链表面试题

1)查找单链表中倒数第K个节点(新浪面试题)

思路:单链表中倒数的第K个节点,也就是该链表中正数第 (链表长度 - K)个节点,那么先算出该链表的长度,附上代码:

// 计算链表的长度
    public int size() {
        int linkedSize = 0;
        if (HEADNODE.next == null) {
            return 0;
        }
        Node temp = HEADNODE;
        while (true) {
            if (temp.next == null) {
                break;
            }
            linkedSize++;
            temp = temp.next;
        }
        return linkedSize;
    }

然后该题也就可以理解为找出单链表中第 index 个节点。附上代码

// 找到单链表中的倒数第K个节点
    public Node getNodeByIndexDesc(int k) {
        Node node;
        // 根据倒数第K个节点,算出该节点在链表中index
        int index = this.size() - k;
        if (index < 0 && index >= this.size()) {
            throw new NullPointerException("此链表不存在倒数第" + k + "个元素");
        } else {
            int count = 0;//计数器
            Node temp = this.getHead().next;
            while (true) {
                if (count == index) {
                    //数到第index的位置是,退出循环
                    break;
                }
                temp = temp.next;  //找元素
                count++;
            }
            node = temp;//最终找到的元素
        }
        return node;
    }

好了,该面试题我们已经成功完成。

2)将单链表反转(腾讯面试题)

该题我们这里就暂时先不给出答案,感兴趣的小伙伴可以自己尝试完成这道题。(最好给自己限定一个时间,比如30分钟内完成)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值