数据结构之数组跟链表

今天我们来聊聊数组跟链表,是非常基础的数据结构。

数组:

数组是无序的元素序列。

示例如下:

int[] arr = {10, 20, 30, 40, 50}

数组的特点是:元素地址是连续的,随机访问速度快。

链表(linked list):

链表(linked list)是由一组被称为结点的数据元素组成的数据结构,每个结点都包含结点本身的信息和指向下一个结点的地址。由于每个结点都包含了可以链接起来的地址信息,所以用一个变量就能够访问整个结点序列。也就是说,结点包含两部分信息:一部分用于存储数据元素的值,称为信息域;另一部分用于存储下一个数据元素地址的指针,称为指针域。


链表分为 【单向链表】 和 【双向链表】。

单向链表:(图片来源于网络)


删除:

(删除 “节点30”)(可以对比数组等也可以加上题目)

删除之前:“节点20” 的后继节点是 “节点30”,而 “节点30”的后继节点是 “节点40”

删除之后:“节点20” 的后继节点是 “节点40”


增加:

(增加 “节点25”)

增加之前:“节点20” 的后继节点是 “节点30” 

增加之后:“节点20” 的后继节点是 “节点25”,“节点25” 的后继节点是 “节点30”


python 实现:

class Node:

    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next

    def set_data(self, data):
        self.data = data

    def get_data(self):
        return self.data

    def set_next(self, next):
        self.next = next

    def get_next(self):
        return self.next


class SinglyLinkedList:

    def __init__(self):
        self.head = None
        self.size = 0

    def add(self, value):
        """
        Add element to list

        Time Complexity:  O(N)
        """
        node = Node(value)
        node.set_next(self.head)
        self.head = node
        self.size += 1

    def _search_node(self, value, remove=False):
        current = self.head
        previous = None

        while current:
            if current.data == value:
                break
            else:
                previous = current
                current = current.next

        if remove and current:
            if previous is None:  # Head node
                self.head = current.next
            else:  # None head node
                previous.set_next(current.next)
            self.size -= 1

        return current is not None

    def remove(self, value):
        """
        Remove element from list

        Time Complexity:  O(N)
        """

        return self._search_node(value, True)

    def search(self, value):
        """
        Search for value in list

        Time Complexity:  O(N)
        """
        return self._search_node(value)

    def size(self):
        """
        Return size of list
        """
        return self.size

双向链表:

它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表

至于 【删除】 和 【添加】的操作,跟 【单向链表】相似。


链表跟数组的区别:

数组的性质:
(1)所有元素在内存中都是相连的。


所以数组存在的问题:
数组想要进行【插入】和【删除】操作的话,是非常麻烦的。因为所有元素在内存中都是相连的。


链表的性质:
(1)链表中的元素可存储在内存的任何地方。
(2)链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起。所以添加元素很容易。

所以链表存在的问题:

在需要读取链表的最后一个元素时,你不能直接读取,因为你不知道它所处的地址,必须先访问元素#1,从中获取元素#2的地址,再访问元素#2并从中获取元素#3的地址,以此类推,直到访问最后一个元素。需要同时读取所有元素时,链表的效率很高:你读取第一个元素,根据其中的地址再读取第二个元素,以此类推。但如果你需要跳跃,链表的效率真的很低。


数组:
数组与此不同:你知道其中每个元素的地址。例如,假设有一个数组,它包含五个元素,起始地址为00,那么元素#5的地址是多少呢?只需执行简单的数学运算就知道:04

参考链接:

(1) http://www.cnblogs.com/skywang12345/p/3561803.html(非常不错的博客)


题目:面试题3:二维数组中的查找(剑指offer)

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有改整数。

当我们需要解决一个复杂的问题时,一个很有效的办法就是从一个具体的问题入手,通过分析简单具体的例子,试图寻找普遍的规律。

首先我们选取数组右上角的数字9。由于 9 大于 7,并且 9 还是第四列的第一个(也是最小的)数字,因此 7 不可能出现在数字 9 所在的列。于是我们把这一列从需要考虑的区域内剔除,之后只需要分析剩下的 3 列。在剩下的矩阵中,位于右上角的数字是 8。同样 8 大于 7,因此 8 所在的列我们也可以剔除。接下来我们只要分析剩下的两列即可。剩下的类似。

总结上述查找的过程,我们发现如下规律:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束;如果该数字大于要查找的数字,剔除这个数字所在的列;如果该数字小于要查找的数字,剔除这个数字所在的行。也就是说如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。
c 语言版本

bool Find(int* matrix, int rows, int columns, int number)
{
     bool found = false;

     if (matrix != NULL && rows > 0 && columns > 0)
     {
          int row = 0;
          int column = column - 1;
          while (row < rows && column >= 0)
          {
               if (matrix[row * column + column] == number)
               {
                    found = true;
                    break;
               }
               else if (matrix[row * columns + column] > number)
                    --column;
               else:
                    ++row;
          }
     }

     return found
}

python 版本

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here

        row=len(array)
        col=len(array[0])
        if row > 0 and col > 0:
            i = 0
            j = col -1
            while i < row and j >= 0:
                if array[i][j] == target:
                    return True
                elif array[i][j] > target:
                    j = j - 1
                else:
                    i = i + 1
        return False

关键点:考察应聘者分析问题的能力。当应聘者发现问题比较复杂时,能不能通过具体的例子找出其中的规律,是能否解决这个问题的关键所在。这个题目只要从一个具体的二维数组的右上角开始分析,就能找到查找的规律,从而找到解决问题的突破口。


关于链表的题目:

题目描述

输入一个链表,从尾到头打印链表每个节点的值。(剑指offer

看到这道题后,很多人的第一反应是从头到尾输出将会比较简单,于是我们很自然地想到把链表中链接结点的指针反转过来,改变链表的方向,然后就可以从头到尾输出了。但该方法会改变原来链表的结构。是否允许在打印链表的时候修改链表的结构?这个取决于面试官的需求,因此在面试的时候我们要询问清楚面试官的要求。

假设不能

接下来我们想到解决这个问题肯定要遍历链表。顺序是从头到尾的顺序,可输出的顺序确实从尾到头。也就是说第一个遍历到的结点最后一个输出,而最后一个遍历到的结点第一个输出。 这就是典型的“后进先出”,我们可以用栈实现这种顺序。 每经过一个结点的时候,把该节点放到一个栈中。当遍历完整个链表后,再从栈顶开始逐个输出结点的值,此时输出的节点的顺序已经反转过来了。

python 代码实现

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        l = []
        head = listNode
        while head:
            l.insert(0, head.val)
            head = head.next
        return l

测试用例:
功能测试:输入的链表有多个结点,输入的链表只有一个结点
特殊输入测试:输入的链表头结点指针为 NULL。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值