2583.二叉树中的第K大层和

目标

给你一棵二叉树的根节点 root 和一个正整数 k 。

树中的 层和 是指 同一层 上节点值的总和。

返回树中第 k 大的 层和(不一定不同)。如果树少于 k 层,则返回 -1 。

注意,如果两个节点与根节点的距离相同,则认为它们在同一层。

说明:

  • 树中的节点数为 n
  • 2 <= n <= 10^5
  • 1 <= Node.val <= 10^6
  • 1 <= k <= n

思路

这个问题的关键在于遍历的同时记录层数并进行累加。由于是升序排列所以取倒数第k个,即MAX-k。

代码

/**
 * @date 2024-02-23 16:06
 */
public class KthLargestLevelSum {
    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    ", left=" + left +
                    ", right=" + right +
                    '}';
        }
    }

    public static void main(String[] args) {
        KthLargestLevelSum main = new KthLargestLevelSum();
        long res = main.kthLargestLevelSum(new TreeNode(1, new TreeNode(2), new TreeNode(3)), 2);
        System.out.println(res);
    }

    public final int MAX = 100000;
    public long[] lacc = new long[MAX];
    public int maxlevel = 0;

    public long kthLargestLevelSum(TreeNode root, int k) {
        traverseSubTree(root, 0);
        if (k > maxlevel + 1) {
            return -1;
        }
        Arrays.sort(lacc);
        return lacc[MAX-k];
    }
    public void traverseSubTree(TreeNode subTree, int level) {
        maxlevel = Math.max(maxlevel, level);
        lacc[level] += subTree.val;
        if (subTree.left != null) {
            traverseSubTree(subTree.left, level + 1);
        }
        if (subTree.right != null) {
            traverseSubTree(subTree.right, level + 1);
        }
    }
}

Arrays.sort使用的是双轴快排(DualPivotQuicksort),时间复杂度是O(nlogn)。

性能

最优的算法是快速选择。因为我们并不需要所有的元素都有序,只要保证k位置的左侧都比k小,右侧均比k大即可,而两侧区间内部是不需要排序的。

889.从前序与后序遍历序列构造二叉树

目标

给定两个整数数组,preorder 和 postorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。

如果存在多个答案,您可以返回其中 任何 一个。

思路

本来看到这个题目觉得没什么新意,前面都已经做过 从前序与中序遍历序列构造二叉树从中序与后序遍历序列构造二叉树这两个题目了,准备快速写一下。没想到,又花费了许多时间。虽然知道大体的方向,但真正写出来还是不那么容易的。对照着一个案例,按照大体思路写完解决方法之后,就急着去测试,结果有的例没有通过,有的数组越界。然后就开始调试,一会这加个判断,那处理一个特例,这个案例通过了,别的又不行了,一来二去就把自己绕晕了。之所以存在这样的情况还是对这个问题的理解不到位,没有一个清晰的思路。

有了前面的两道题的求解经验,我们知道这个题还是用递归求解更容易一些。求解的核心是遍历先序/后序序列,一个从前向后,一个从后向前。将遍历到的每一个节点去中序序列中找到相应位置然后划分左/右子树,然后递归处理子树。其实这里容易忽略一个问题,就是左/子树的节点一定会在先序/后序序列随后遍历中出现。可以根据这个来明确临界条件,否则容易漏掉或者弄错左右以及父节点。这也就是为什么先序序列先遍历左子树,后序先遍历右子树的原因。因为游标顺序移动,刚好可以覆盖相应的子树。刚开始我还想着分别在先序和后序维护两个游标,这是不可行的。

说回到这个题目,它不像前面两个那样可以明确左右子树。前面说前序序列的第二个节点是其左子树或右子树的根节点,并非是无法确定,而是需要结合实际情况看是否存在左子树,如果存在则一定是左子树根节点。这个可以在中序序列中找到相应位置就知道了。但是对于这个题而言左右是无法确定的,很简单的例子 [1,2][2,1]

我们的思路是遍历先序序列,找到其在后序序列的位置,该位置减1则是可能的右根节点,反查其在先序序列中的位置。这样我们就得到了一个左子树区间或者单个节点(无法确定左右)。这样我们就可以递归构建左子树,对于单个节点的情况需要将搜索范围扩展到序列结尾,否则可能反查不到节点在先序序列中的位置。

代码

/**
 * @date 2024-02-23 10:05
 */
public class BuildBinaryTreeFromPrePost {
    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    ", left=" + left +
                    ", right=" + right +
                    '}';
        }
    }

    public static int preCursor = 1;

    public static void main(String[] args) {
        BuildBinaryTreeFromPrePost main = new BuildBinaryTreeFromPrePost();
//        int[] preorder = new int[]{1,2,4,5,3,6,7};
//        int[] preorder = new int[]{3, 9, 20, 15, 7};
//        int[] preorder = new int[]{1,2};
//        int[] preorder = new int[]{2,1,3};
//        int[] preorder = new int[]{4,2,1,3};
        int[] preorder = new int[]{3,4,2,1};
//        int[] preorder = new int[]{1};
//        int[] postorder = new int[]{4,5,2,6,7,3,1};
//        int[] postorder = new int[]{1};
//        int[] postorder = new int[]{9, 15, 7, 20, 3};
//        int[] postorder = new int[]{2,1};
//        int[] postorder = new int[]{3,1,2};
//        int[] postorder = new int[]{3,1,2,4};
        int[] postorder = new int[]{2,1,4,3};
        System.out.println(main.constructFromPrePost(preorder, postorder));
    }

    public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
        TreeNode root = new TreeNode(preorder[0]);
        if (preorder.length == 1) {
            return root;
        }
        int rightRootPreIndex = 0;
        for (int i = 0; i < preorder.length; i++) {
            if (postorder[postorder.length - 2] == preorder[i]){
                rightRootPreIndex = i;
                break;
            }
        }
        // 注意当start == end时,搜索长度应扩展到整个数组长度,否则会导致查询不到当前节点的右子树根节点。
        root.left = buildSubTree(preorder, postorder, preCursor, rightRootPreIndex == preCursor ? preorder.length:rightRootPreIndex);
        if (preCursor >= rightRootPreIndex && preCursor < preorder.length) {
            root.right = buildSubTree(preorder, postorder, rightRootPreIndex, preorder.length);
        }
        return root;
    }

    public TreeNode buildSubTree(int[] preorder, int[] postorder, int start, int end){
        TreeNode root = new TreeNode(preorder[preCursor]);
        int rightRootPostIndex = 0;
        int rightRootPreIndex = 0;
        for (int i = 0; i < postorder.length; i++) {
            if (root.val == postorder[i]){
                rightRootPostIndex = i;
                preCursor++;
                break;
            }
        }
        if (rightRootPostIndex > 0) {
            for (int i = start; i < end; i++) {
                if (postorder[rightRootPostIndex - 1] == preorder[i]) {
                    rightRootPreIndex = i;
                    break;
                }
            }
            if (preCursor < rightRootPreIndex) {
                root.left = buildSubTree(preorder, postorder, preCursor, rightRootPreIndex == 0 ? preorder.length : rightRootPreIndex);
            }
            if (preCursor >= rightRootPreIndex && preCursor < end && rightRootPreIndex !=0) {
                root.right = buildSubTree(preorder, postorder, rightRootPreIndex, end);
            }
        }
        return root;
    }
}

先序序列的初始条件是直接从第二个位置开始的,直接从后序序列的倒数第二个反查其在先序序列的位置。

性能

LCP24.数字游戏

目标

小扣在秋日市集入口处发现了一个数字游戏。主办方共有 N 个计数器,计数器编号为 0 ~ N-1。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组 nums。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。

主办方请小扣回答出一个长度为 N 的数组,第 i 个元素(0 <= i < N)表示将 0~i 号计数器 初始 所示数字操作成满足所有条件 nums[a]+1 == nums[a+1],(0 <= a < i) 的最小操作数。回答正确方可进入秋日市集。

由于答案可能很大,请将每个最小操作数对 1,000,000,007 取余。

思路

老实说这道题我花了好一会儿才弄清楚题目要求,最后解法还因为超时没有通过。

先解释一下目标吧,实际上需要求解的是每一个位置上的局部最优解。所谓局部指仅考虑当前位置及之前的所有位置的操作次数。所谓最优是指初始序列达到满足条件的状态(公差为1的等差数列)时所做操作总和的最小值。如果有N个计数器,那么就有N个满足条件的序列,有可能前面操作最少的序列并不是后面操作最少的序列,即前面位置的总操作次数可能并不是当前最优解的一部分。可以参考上面图片的示例3。

我的思路就是直接暴力破解,循环遍历输入的数组,在第i次遍历中,分别将(0 ~ i-1)位置上的元素作为基点k,左侧的序列依次减1,右侧的依次加1。在第k个序列中求解(0 ~ i-1)位置上操作次数的累加和。在第j个位置上的操作次数为 nums[k]-(k-j)。

代码

public static int[] numsGame2(int[] nums) {
    int[] res = new int[nums.length];
    // 累加k序列的操作总和
    int[] acc = new int[nums.length];
    for (int i = 1; i < nums.length; i++) {
        for (int k = 0; k <= i; k++) {
            int temp = acc[k];
            // 增加一些判断跳过一些计算
            if(temp >= res[i] && res[i] != 0){
                continue;
            }
            // 这里看似套了3个循环,但其实只有在k序列第一次出现时才会循环i次,那么前面的i-1次只需从acc中累计即可,实际时间复杂度是O(n^2)
            for (int j = temp == 0 ? 0 : i; j <= i; j++) {
                temp += Math.abs(nums[j] - (nums[k] - (k - j)));
            }
            acc[k] = temp;
            if (k == 0) {
                res[i] = temp;
            } else {
                res[i] = Math.min(res[i], temp);
            }
        }
        res[i] = res[i] % 1000000007;
    }
    return res;
}
acc a b c d
acc[0] 0 |b-a-1| |c-a-2| |d-a-3\
acc[1] |a-b+1| 0 |c-b-1| |d-b-2\
acc[2] |a-c+2| |b-c+1| 0 |d-c-1\
acc[3] |a-d+3| |b-d+2| |c-d+1| 0

如上表所示,如果序列是[a,b,c,d],基点k等于c的时候,前2次从acc累加,最后一次需要重新累加。时间复杂度为O(n^2)。这和我们常见的外层循环N次,内层循环N次不同。循环的计算次数序列为1、3、5、7...,根据等差数列求和公式:Sn = n × a1 + (n × (n-1)/2) × d,当 d=2a1=1 时,Sn = n^2

性能

由于题目给出的数组最大长度是10^5,暴力求解是不可行的。也尝试了增加条件判断跳过一些计算但还是无法降低问题的规模。于是就开始怀疑是否前面的计算结果是否与后面的计算有关联,可以简化后面的计算?更优的算法复杂度应为O(nlogn)O(logn)O(n)。我思考了很久,应该是存在知识盲区了。我去看了答案,涉及到求中位数。其实一开始有想过中位数,方差均值这些,但是没想到和这个最优解有什么关系。今天没时间了,抽空再看看吧。

106.从中序与后序遍历序列构造二叉树

目标

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

思路

有了前面 从前序与中序遍历序列构造二叉树 的经验这个问题就很好处理了。二叉树的后序遍历指先访问左子树、再访问右子树,最后访问根节点。

只需从后序序列的最后一个元素向前遍历即可,最后一个是根节点,接着是右子树、左子树的根节点 (注意这里是先右后左)。

还是在中序遍历中找到该根节点,然后其左侧的为左子树,右侧为右子树。依次递归遍历右子树与左子树,在递归方法中根节点取后序序列的一个节点即可。

代码


/**
 * @date 2024-02-21 14:12
 */
public class BuildBinaryTreeFromMidPost {
    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    ", left=" + left +
                    ", right=" + right +
                    '}';
        }
    }

//    static int[] inorder = new int[]{9, 3, 15, 20, 7};
    static int[] inorder = new int[]{2,3,1};
//    static int[] inorder = new int[]{-1};

//    static int[] postorder = new int[]{9, 15, 7, 20, 3};
    static int[] postorder = new int[]{3,2,1};
//    static int[] postorder = new int[]{-1};

    static int postCursor = postorder.length - 1;

    public static void main(String[] args) {
        int rootIndex = 0;
        TreeNode root = new TreeNode(postorder[postCursor]);
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == postorder[postCursor]) {
                rootIndex = i;
                // 一定能够找到
                postCursor--;
                break;
            }
        }
        int leftEndIndex = rootIndex - 1;
        int rightStartIndex = rootIndex + 1;
        // 注意这个postCursor应该是共享变量,原先是想以参数传递的,但是发现递归的时候还要将它传回来,改成了共享变量
        // 这里是先遍历右子树再左子树
        if (rightStartIndex < inorder.length) {
            root.right = traverseSubTree(inorder, rightStartIndex, inorder.length);
        }
        if (leftEndIndex >= 0) {
            root.left = traverseSubTree(inorder, 0, leftEndIndex);
        }
        System.out.println(root);
    }

    public static TreeNode traverseSubTree(int[] inorder, int start, int end) {
        TreeNode subRoot = new TreeNode(postorder[postCursor]);
        int rootIndex = start;
        for (int i = start; i <= end; i++) {
            if (inorder[i] == postorder[postCursor]) {
                rootIndex = i;
                postCursor--;
                break;
            }
        }
        int leftEndIndex = rootIndex - 1;
        int rightStartIndex = rootIndex + 1;
        // 临界条件判断,这里应该是<=,并且排除掉inorder.length
        // 这里是先遍历右子树再左子树
        if (rightStartIndex <= end && rightStartIndex != inorder.length) {
            // 这里的结束条件传end
            subRoot.right = traverseSubTree(inorder, rightStartIndex, end);
        }
        if (leftEndIndex >= start) {
            subRoot.left = traverseSubTree(inorder, start, leftEndIndex);
        }
        return subRoot;
    }
}

性能

105.从前序与中序遍历序列构造二叉树

目标

给定两个整数数组 preorder 和 inorder,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

思路

首先要明白先序遍历与中序遍历的概念。所谓二叉树的 先序遍历 指的是 先访问根节点,然后遍历左子树,再遍历右子树中序遍历 则是 先遍历左子树,再根节点,然后右子树

很容易想到使用递归,关键点是数组左右边界的维护,临界条件的判断。

注意到 先序遍历数组的第一个节点一定是根节点,其后面的节点则是其左子树或右子树的根节点

于是先根据根节点在中序遍历中找到该根节点,然后其左侧的为左子树,右侧为右子树。依次递归遍历左子树与右子树(注意判断边界条件),在递归方法中根节点取刚才根节点的后一个节点(需要一个共享变量来记录位置)。

代码


/**
 * @date 2024-02-20 11:43
 */
public class BuildBinaryTree {

    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    ", left=" + left +
                    ", right=" + right +
                    '}';
        }
    }

    static int[] preorder = new int[]{3, 9, 20, 15, 7};
//    static int[] preorder = new int[]{1,2,3};
//    static int[] preorder = new int[]{-1};

    static int[] inorder = new int[]{9, 3, 15, 20, 7};
//    static int[] inorder = new int[]{2,3,1};
//    static int[] inorder = new int[]{-1};

    static int preCursor = 0;

    public static void main(String[] args) {
        int rootIndex = 0;
        TreeNode root = new TreeNode(preorder[preCursor]);
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == preorder[preCursor]) {
                rootIndex = i;
                // 一定能够找到
                preCursor++;
                break;
            }
        }
        int leftEndIndex = rootIndex - 1;
        int rightStartIndex = rootIndex + 1;
        if (leftEndIndex >= 0) {
            root.left = traverseSubTree(inorder, 0, leftEndIndex);
        }
        // 注意这个preCursor应该是共享变量
        if (rightStartIndex < inorder.length) {
            root.right = traverseSubTree(inorder, rightStartIndex, inorder.length);
        }
        System.out.println(root);
    }

    public static TreeNode traverseSubTree(int[] inorder, int start, int end) {
        TreeNode subRoot = new TreeNode(preorder[preCursor]);
        int rootIndex = start;
        for (int i = start; i <= end; i++) {
            if (inorder[i] == preorder[preCursor]) {
                rootIndex = i;
                preCursor++;
                break;
            }
        }
        int leftEndIndex = rootIndex - 1;
        int rightStartIndex = rootIndex + 1;
        if (leftEndIndex >= start) {
            subRoot.left = traverseSubTree(inorder, start, leftEndIndex);
        }
        // 临界条件判断,这里应该是<=,并且排除掉inorder.length
        if (rightStartIndex <= end && rightStartIndex != inorder.length) {
            // 这里的结束条件传end
            subRoot.right = traverseSubTree(inorder, rightStartIndex, end);
        }
        return subRoot;
    }
}

性能

我的目标是能解决问题就好,看了下性能分布还有优化的空间,官网还给出了迭代的解法,没时间看。递归应该是更容易理解的方法了。希望能够坚持下去吧。

有感而发

最近在书中读到乔布斯的一段话特别有感触,他说:"You can't connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future. You have to trust in something - your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life"。在学习的过程中,对于书中不理解的地方,可以先放过去,暂时容忍自己的无知。等到通读全文后回头再看时,才能将知识点连起来。这就是所谓读书的第一层境界,“独上高楼,望尽天涯路”。如果没有一个整体的视角,陷入细枝末节,注定事倍功半,也往往坚持不到最后。在生活中也是一样,但行好事,莫问前程。应该相信直觉、相信命运、相信生活、相信因果,这从没让乔布斯失望,也应该不会让我失望吧。

好久没有更新了

最近在备考网工,系统地学了一下计算机网络,上次完整地看完一本书还是在学校的时候。之所以考这个就是为了以目标为驱动的学习。希望可以一次考过,虽然这代表不了什么。

人很难孤独地忍受自己,没有对精神事物的追求,人便会无聊。
无聊是一切罪恶的源头,它让人感到虚无,进而使人放纵。
人并非是理性生物,我们由情感驱使,被偏见支配,傲慢与虚荣是我们的动力之源。

数据通信基础

基本概念

信源、信宿、信道
信息进入信道要变换为适合信道的传输形式,进入信宿时还要变换为适合信宿接收的形式。
信道的物理性质影响通信速率与传输质量,不同的信道外界干扰(噪声)也不相同 。
信源模拟信号 模拟信道传输 称为模拟通信
信源模拟信号 数字信号传输 称为数字通信
信源数字信号 无论如何传输 均称为数据通信
模拟信道传输需要调制,调制信号频谱窄因此信道利用率高,但是传输过程中会衰减,还会受到噪声干扰,如果使用放大器还会同时放大噪声。
数字信道可以直接传输二进制数据或者二进制编码的数据、或者传输数字化了的模拟信号(采样、量化、编码),只要没有畸变到不可辨认的程度就可以进行恢复,并且可以通过差错控制消除个别差错,因此数字传输对于保证信号不失真的传送非常有好处。另外,大规模集成,设备便宜。但是频带宽信道利用率低。
数字信道抗噪,但也是会衰减的。

信道特性

信道带宽、尼奎斯特定理(有限带宽无噪声信道的最大码元速率,波特率)B = 2W、 码元信息量、数据速率
香农定理(有噪声信道的极限数据速率)
误码率
信道延迟,电缆中传播速率约为77%光速,200m/us,除了考虑传播速率还要考虑通信线路长度。卫星信道延迟大约270ms。

传输介质

  • 双绞线
  • 同轴电缆(基带传输、宽带系统)基带数字信号为何衰减的快

大致是这样的:数字基带的低频信号代表长“0”或长“1”,数字基带信号的高频部分1010…,其他成分介于两者之间。这样看来整个的相对频谱很高。有线传输线的特性不支持这种信号,造成远端信号(比如低频信号直流偏移)这样畸变巨大,即使使用均衡器都不行。所以必须用某种信道编码(比如扰码减小相对带宽)达到传输目的。
站在信号与系统的角度来看,无线信道就是带通系统,有线传输目前很多都是低通系统(光纤是属于另类的带通系统,有不同的波段窗口)。

这是因为电磁场在传输线里面传播,一般而言,频率越高衰耗越大。以常见的网线和同轴电缆为例,而这些传输线的介质主要以铜芯为主,电磁场在铜介质里面传播,由于铜是良导体,高频电磁波频率越高,趋肤效应越明显,表面电阻越高,由此导致的损耗就越高。
大概就是这么个意思了,如果还有疑惑,请查看本科的 传输线理论 和 电磁场理论

  • 光缆
  • 无线信道(红外,高带宽传输距离有限;微波:波长1mm ~ 1m是分米波、厘米波、毫米波的统称,频率300MHz ~ 300GHz;短波:波长10m ~ 100m 频率3MHz~30MHz;超短波:波长 1m ~ 10m 频率30MHz ~ 300MHz 甚高频 )

传播途径有:天波与地波(地表面波、直接波、地面反射波)

分米波 特高频(300MHz ~ 3GHz 10cm ~ 1m)

无线局域网使用了甚高频和特高频的电视广播频段(书上说这属于短波,可能界限并不这么明显吧)

无线电频谱和波段划分表

段号 频段名称 频段范围 波段名称 波长范围
1 极低频(ELF) 3 ~ 30赫(Hz) 极长波 100 ~ 10兆米
2 超低频(SLF) 30 ~ 300赫(Hz) 超长波 10 ~ 1兆米
3 特低频(ULF) 300 ~ 3000赫(Hz) 特长波 100 ~ 10万米
4 甚低频(VLF) 3 ~ 30千赫(KHz) 甚长波 10 ~ 1万米
5 低频(LF) 30 ~ 300千赫(KHz) 长波 10 ~ 1千米
6 中频(MF) 300 ~ 3000千赫(KHz) 中波 AM广播 被大气层反射 10 ~ 1百米
7 高频(HF) 3 ~ 30兆赫(MHz) 短波 短波广播 100 ~ 10米
8 甚高频(VHF) 30 ~ 300兆赫(MHz) 米波 FM广播 10 ~ 1米
9 特高频(UHF) 300 ~ 3000兆赫(MHz) 分米波 10 ~ 1分米
10 超高频(SHF) 3 ~ 30吉赫(GHz) 厘米波 10 ~ 1厘米
11 极高频(EHF) 30 ~ 300吉赫(GHz) 毫米波 10 ~ 1毫米
12 至高频(THF) 300 ~ 3000吉赫(GHz) 丝米波 10 ~ 1丝米

调幅广播由于只需要一个频率的载波,通过这个频率的载波的幅度变化来传输信号,所以可以采用500-1300khz这段频率较低且频宽较窄的中波进行广播,而中波具有可以被大气层反射的特性,因此可以传播得非常远。顺带一提,短波广播也是调幅广播,而短波在大气层的反射高度比中波更高,所以距离也比中波更远,足以跨过地球。调频广播则是通过确定一个中心频率,根据收到的实际频率相对该中心频率的偏差程度来传递信号,所以会占用一大段频率,不适合像调幅广播那样用范围狭窄资源紧凑的中波和短波,而是使用范围较大资源较充裕的超短波和微波,而超短波和微波不能被大气层反射,会直接穿透到宇宙空间去,所以范围比调幅广播要小得多。关于抗干扰性,由于调频广播不依赖电波的幅度来传输信号,所以即使有相同频率的干扰源扰乱了电波的幅度,也不会对调频广播形成噪声,调幅就不行了,只要有干扰源影响到了电波的幅度,那信号就毁了。因此,调频的音质比调幅要好。

频率低传得远些,频率越低,波长就越长,
幅值越大信号越强,当然传得也越远,
波长越长也传得远
无线电波按其波长可分为四个波段。与红外线邻近的波长最短的波段称为微波(microwave),波长约为0.1mm ~ 1m;比微波的波长长的波段依次为短波(short wave,波长为1m ~ 102m)、中波(medium wave,波长为102 ~ 103m)和长波(long wave ,波长为103 ~ 105m)。在实际应用中,不同波段落的无线电波的传播方式和应用领域各不相同。
由于地面、高山、电离层等对各波段无线电波的吸收、反射、透射等性能的不同,无线电波在空间的传播通常采用三种方式:地波传播、天波传播、空间波传播
一、 地波传播
地波传播是无线电波沿地球表面附近空间的传播,传播时无线电波可随地球表面的弯曲而改变传播方向。
地球表面分布有起伏不平的山峦,以及高低不平的建筑物等障碍物,无线电波只有绕过这些障碍物,才能传到较远的地方。当电磁波的波长大于或相当于障碍物的尺寸时,波的衍射性能较好,即可绕过障碍物。因此,长波能很好地绕过几乎所有的障碍物,而中波和短波中部分波长较长的波还能较好地绕过不太大的障碍物,其余部分的短波和微波的绕射能力就很差。
二、天波传播
天波传播是无线电波通过电离层反射而进行的传播。电离层反射特性还与无线电波的波长有关,波长越长,则越容易反射。所以,长波、中波和短波都可以被电离层反射,而微波和超短波则基本上穿透电离层而不被反射。天波传播最适合于短波的传播,因为波长太短的超短波,电离层不反射;而对于长波,则电离层的吸收又太强。
三、空间波传播
空间波传播是无线电波像光那样沿直线的传播。由于地球近似球体,因此,空间波是传不远的,传播的最远距离不能超过视线距离
可见,直线传播的空间波是不能进行远距离传播的。当然,无线电波除了直接从发射天线传播到接收天线外,也可以经过地面反射而传到接收天线。因此,接收天线接收到的应是这两种波的合成波。微波与超短波一般采用空间波传播。
地波、天波、空波这三种传播方式,适合于不同波长无线电波的传播。长波一般采用地波传播。这是因为长波的绕射能力强,且大气对它的吸收少,因此比较适合地波传播。另外,长波虽然不会穿透电离层,但由于电离层对其有强烈的吸收作用,所为不适合天波传播。长波传播具有稳定性好、受干扰小、传播距离远等优点,超长波甚至能做环球传播,但长波需要庞大的天线设备,实际应用不多,通常只用于潜艇和远洋航行的通信等。
中波可用天波与地波两种方式传播。白天由于电离层吸收作用较大,主要靠地波传播。晚上电离层吸收作用减少,天波传播可大大增加传播距离。所为,中波昼夜信号强度差别较大,不适合远距离通信,而常用于国内广播等。
短波主要靠天波传播,短波经电离层反射时,电离层对他的吸收作用较小,故经电离层和地面的多次连续反射,可传播到很远的地方。短波传播的最大缺点是不稳定。一般用作各种长、短距离的通信。超短波与微波的绕射能力差,又会穿透电离层,因此不适合地波或天波传播,只适合空间波传播。由于空间波传播的距离有限,为增加传播距离,可采用增高发射天线和接力通信等方法

数据编码

自定时编码、曼彻斯特编码等

数字调制技术

用数字信号调制模拟信号称为数字调制,调频(FSK)、调幅(ASK)、调相(PSK)、正交幅度调制(QAM)
将模拟数据转化为数字信号的过程称为模拟数据的数字化,常用的技术为PCM,简称脉码调制(采样、量化、编码)。
扩频通信(频率跳动扩频、直接序列扩频)

通信与交换方式

通信方向

单工、半双工、双工

同步方式

异步传输(起止式)
同步传输(短距离高速传输)

交换方式

电路交换(空分、时分)、报文交换、分组交换(数据报、虚电路)

多路复用技术

频分、时分、波分、码分(CDMA)
数字传输系统:
美国与日本的通信标准,贝尔系统的T1载波
其它地区,E1载波
光纤线路的多路复用标准:SONET、SDH

计算机网络概论-2

计算机网络体系结构

计算机网络发展至今形成了非常庞大而复杂的体系结构 系统,对付这种复杂系统的常规方法是 使用分层来处理把系统组织成分层的体系结构,把很多相关的功能分解开来,逐个予以实现。在分层体系结构中,每一层都是一些明确定义的相互作用的集合,称为对等协议。层之间的界限是另一些相互作用的集合,叫做接口协议。
研究计算机网络的基本方法是全面深入地了解计算机网络的功能特性,即计算机网络是如何在两个端用户之间建立连接通路的。理解了计算机网络的功能特性才能掌握各种网络的特点,才能了解网络运行的原理。

计算机网络的功能特性

首先计算机网络必须提供通讯主体源节点与目标节点之间的连接传输线路。传输线路可能要经过一些中间节点。如果远程联网还需要公用通信线路(地面链路或卫星链路),如果是模拟信号还需要调制,因此计算机网络还需要提供与调制解调器物理与电气的接口
由于计算机通信的特点:突发性与间歇性,通信链路应有较高的带宽并由许多节点共享以提高利用率。报文交换技术与分组交换技术,对传输信息流进行分组,加入控制信息(差错控制与地址),并把分组正确的传送到目的地(有多个转发节点还需要有根据网络配置和交通情况决定路由的功能)。多节点同时要求发送分组时要有冲突仲裁
控制信息数据包在网络中通过一个一个节点正确向前传送的功能叫做DLC
流量控制与拥塞控制
会话管理:会话的建立、释放、双向通信管理
字符集、数据格式、安全保密措施统一的协议

用户输入的字符流根据协议转换(字符集/编码与数据格式 ANSI.1),加上控制位与顺序号进行会话管理。再进行分组,加入地址字段和校验字段等。经过modem变换送入公共载波线路传送。
通信过程经过分解后得到的功能元素总是成对出现的,每一对功能元素相互通信,它们之间的协议不涉及相邻层次的功能。例如,一对Modem之间的对话不涉及传输线路的细节,也不必了解它们传输的比特流的意义。
物理层已经是最低层了,怎么能不涉及传输线路细节呢?实际上这里所说的传输线路细节指的是物理线路固有的属性,非要说的话这也是一种协议,只不过它不是由人来规定的。传输介质组成了所谓的第0层。

相邻节点 物理与链路
端节点 应用、表示、会话、打包拆包(分组?)
所有节点 寻址与路由

高层
寻址-路由-分组
低层

复杂性:
有些功能会出现在一个以上的层次中
端用户的数据流选择在哪一层合并
同一节点的层次之间还有控制信息的通信
在特定场景下某些功能层可能很简单甚至没有

至此已经引入了功能层次的概念,对等层之间按规定的协议通信,相邻层之间按接口关系提供服务和接受服务。把实现复杂的网络通信过程的各种功能划分成这样的层次结构,就是网络的分层体系结构。

OSI/RM

所谓开放系统,是指遵从国际标准的、能够通过互联而相互作用的系统。1979年ISO公布了该模型,同时CCITT认可并采纳了这一国际标准的建议文本X.200。OSI/RM为开放系统互联提供了一种功能结构框架,ISO7498对它作了详细的规定和描述。

物理电路控制子系统 分组交换子系统 传输控制子系统
分组交换在链路与网络层都有?