如何去掉行首行尾的空格

如何去掉行首行尾的空格

更多干货参见本人技术博客:https://blog.zhenglin.work

通过sed替换方法去掉行首或行尾的空格

$ echo   -e  "Hello  Word    "  | sed 's#\s*##;s#\s*$##'
Hello  Word
  • #可以与/互换,避免混淆这里统一用#来表示分隔符
  • \s匹配任何空白字符,包括空格、制表符、换页符等等,等价于[ \f\n\r\t\v]
  • \S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]

其他方法

在GitHub浏览代码时,发现https://github.com/dylanaraps/pure-bash-bible一个特别好的bash代码应用实例,上面截取字符串方法如下:

  • 去掉行首和行尾的空格
trim_string() {
    # Usage: trim_string "   example   string    "
    : "${1#"${1%%[![:space:]]*}"}"
    : "${_%"${_##*[![:space:]]}"}"
    printf '%s\n' "$_"
}

结果:

$ trim_string "    Hello,  World    "
Hello,  World

代码写的很简洁,注释太少;这里做一下注解 ,方便自己理解和以后使用,可以给大家提供一个参考

  • 首先看方法体格式

method(){

: 参数1

: 参数2

}

它里面有注解:The : built-in is used in place of a temporary variable. 用内置函数冒号替代临时变量 ;和${parameter:word} 这种用法类似,把处理后得到的值存储到冒号中了; 这种格式方法,值得借鉴。

  • “$_” 是保存之前执行命令 最后一个参数
  • 字符串截取可以参考 https://www.cnblogs.com/xwdreamer/p/3823463.html
  • 这是假设一个字符串为: “两个空格Hello空格Word三个空格”,开头的两个空格命名为A;结尾三个空格命名为B;即“AHello WordB”
  • 字符串截取知识掌握后,再来逐步分析${1#"${1%%[![:space:]]*}"} 作用 ,这种层层取值变量,都是先计算最内部的值(如果是=赋值都是先算右边),然后再算外层的;${1%%[![:space:]]*},${1}是变量,’%%[![:space:]]*’ 个是从右往左进行截取,直到截取值为最后几个空格(把最后一个非空格都截掉了就剩开头两个空格即A了) ;然后’${1#A}’,再次字符串截取,相当于只截取掉A(开头两个空格),得到“Hello WordB”
  • '${%"${##[![:space:]]}"}’,’${_}‘表示存储之前操作后得到的变量;’${_##[![:space:]]}‘从左往右截取,所有非空格都截取掉,只保留最后几个空格即B(结尾的三个空格),然后变为’${_%B}'这个就是把结尾处B(三个空格)截取掉。

发散与扩展

[![:space:]]能不能像sed中用到的正则换成\S,尝试了一次,没有成功,然后又替换成[^ \f\n\r\t\v] 这种非简写的格式,最后也可以实现这个效果;printf 格式输出换成echo也可以 。

trim_stringw() {
    # Usage: trim_string2 "   example   string    "
    : "${1#"${1%%[^ \f\n\r\t\v]*}"}"
    : "${_%"${_##*[^ \f\n\r\t\v]}"}"
    echo  "$_"
}

但是如果换成(无法实现效果):

trim_stringb() {
    # Usage: trim_string "   example   string    "
    : "${1#[[:space:]]*}"
    : "${_%*[[:space:]]}"
    printf '%s\n' "$_"
}	

就实现不了这个效果;

去掉所有多余空格,中间的空格只保留一个空格

trim_all() {
    # Usage: trim_all "   example   string    "
    set -f
    set -- $*
    printf '%s\n' "$*"
    set +f
}

这个方法比较好理解,通过set – $* 去掉了多余的空格,每个参数间只保留一个空格,set -f /+f 取消/增加通配符使用后,方便格式化;所以上面方法可以写成:

trim_all() {
    set -- "$@"  #或set -- "$*"
    echo "$@"  #或echo "$*"
}

如果用$@的话,用引号包起来,表示一个字符串整体,$*用不用引号包起来都表示一个字符串。

更新更详细的博客,见博主官网:
https://blog.zhenglin.work

<think>我们有一个二叉树,需要按照层级遍历(BFS)的方式输出每一层的节点值,但要去除空节点(在输入中空节点用-1表示),并且输出格式要求每输出一层的节点值,行尾不能有多余空格。根据引用[2]和引用[3]中关于二叉树的层级遍历和完全二叉树的性质,我们可以采用队列来实现BFS。但是注意,题目中要求去除空节点(-1),并且在输出时每层节点值之间用空格隔开,但行尾不能有空格。思路:1.先,我们需要构建二叉树。根据引用[4],我们可以知道根节点是输入中没有出现过的节点(但这种方法可能不通用)。然而,在引用[3]的代码中,他们通过数组和完全二叉树的性质构建树:节点i的左子节点为2i,右子节点为2i+1(这里i从1开始编号)。但是,题目没有说一定是完全二叉树,所以我们需要另一种方法。但是,用户的问题描述中并没有给出具体的输入格式和如何构建二叉树。因此,我们需要假设输入格式。根据常见的二叉树输入,这里我们假设输入是一个数组,表示按照完全二叉树的层序遍历顺序给出的节点值,其中空节点用-1表示。例如,输入数组:[1,2,3,-1,-1,4,5]表示:1/ \23/ \45但是,注意:题目要求去除空节点,所以输出时只输出非-1的节点。2.层级遍历(BFS)时,我们仍然按照层序遍历的顺序,但是遇到空节点(-1)就跳过,不加入队列,也不输出。但是,这里有一个问题:我们如何知道哪些节点是有效的?因此,我们在构建树的时候,可以只构建非空节点,然后进BFS。但是,题目要求输出每层节点值,并且每层单独一行尾空格。3.另一种方法是直接根据数组进层级遍历,而不显式构建树。因为输入数组已经是层序遍历的顺序,但是包含空节点(-1)。我们可以利用队列,但是队列中存储的是节点的索引(在数组中的位置),然后按照完全二叉树的规则(即如果当前节点索引为i,则左子节点索引为2*i+1,右子节点索引为2*i+2,注意这里索引从0开始)来遍历。但是这种方法要求数组是严格按照完全二叉树的层序遍历(包括空节点)给出的。4.但是,用户的问题描述中并没有明确输入格式。我们假设输入是一个整数数组,表示二叉树的层序遍历(包括空节点用-1表示)。那么,我们可以直接遍历这个数组,但是需要按层输出。然而,直接按数组顺序输出无法区分层次,因为数组是连续的。所以我们需要知道每一层的起始位置。5.在完全二叉树中,第k层(从0开始)的节点索引范围是[2^k-1,2^(k+1)-2]。但是,如果二叉树不是完全二叉树,那么数组中可能包含空节点,我们需要跳过空节点,但又要保持层次结构。所以,我们仍然需要模拟BFS过程。6.因此,我们采用BFS,但是队列中存储的是节点值(非空)和节点在数组中的索引(或者我们不需要索引,因为数组是层序的,但我们需要知道下一层节点的位置?)实际上,如果我们用队列来模拟,那么队列中存储的是节点的值(非空)以及该节点在树中的位置(用于计算其左右子节点的索引)。但是,我们也可以不存储位置,而是根据数组索引关系。7.更直接的方法:使用队列进BFS,但队列中存储的是数组的索引。我们从根节点(索引0)开始,将索引0加入队列。然后,每次从队列中取出一个索引i,如果该索引对应的值不为-1,则输出(注意,不是立即输出,而是按层收集),然后将其左右子节点的索引(2*i+1和2*i+2)加入队列,前提是这些索引在数组范围内,并且对应节点不为空(即不为-1)?但是,这样会跳过空节点,但注意,如果子节点是空节点,我们就不加入队列。这样,我们遍历队列,按层输出。8.但是,这样按层输出需要知道每一层的结束。我们可以在每一层开始的时候记录当前队列的大小,然后处理这一层的所有节点。具体步骤:a.初始化一个队列,将根节点(索引0)加入队列(如果根节点不为-1)。b.当队列不为空时:-记录当前队列的大小size(表示当前层的节点数)-初始化一个列表currentLevel用于存储当前层的节点值(或者直接构建字符串,但为了处理空格,我们先用列表收集)-循环size次:从队列中取出一个索引i将数组中该索引的值加入currentLevel(因为非空节点才会在队列中,所以直接加入)然后检查左子节点(索引为2*i+1)和右子节点(索引为2*i+2):如果左子节点索引在数组范围内且值不为-1,则将其索引加入队列右子节点同理-将currentLevel中的值用空格连接成一个字符串,输出(这样行尾不会有空格,因为currentLevel只收集了非空节点)-然后继续下一层9.但是,这种方法存在一个问题:数组可能不是完全二叉树,但是输入是按照完全二叉树的方式(即空节点用-1填充)给出的。所以,我们可以通过索引访问子节点,只要索引在数组范围内。10.注意:如果数组长度为0,则输出空。11.举例:输入数组为[1,2,3,-1,-1,4,5]根节点索引0,值为1,加入队列。第一层:队列大小为1,处理索引0,取出0,得到1,然后左子节点索引1(2*i+1=1),值为2(不为-1,加入队列);右子节点索引2(2*i+2=2),值为3(加入队列)。第一层输出:[1] ->"1"第二层:队列大小为2,先处理索引1:值为2,左子节点索引3(2*1+1=3)值为-1(跳过),右子节点索引4(2*1+2=4)值为-1(跳过),所以没有子节点加入队列。然后处理索引2:值为3,左子节点索引5(2*2+1=5)值为4(加入队列),右子节点索引6(2*2+2=6)值为5(加入队列)。第二层输出:[2,3] ->"23"第三层:队列大小为2,处理索引5:值为4,左子节点索引11(超出范围,跳过),右子节点索引12(超出范围,跳过)。处理索引6:值为5,同理没有子节点。第三层输出:[4,5] ->"45"但是,这样输出的第二层是2和3,第三层是4和5,符合要求。12.但是,我们如何确保子节点的索引正确?因为数组是层序存储的,所以按照完全二叉树的规则,节点i的左子节点在2*i+1,右子节点在2*i+2。所以,只要数组是按照完全二叉树的层序(包括空节点)给出的,就可以这样计算。13.然而,题目要求去除空节点(-1),所以我们在加入队列时,只加入非空节点(即值不为-1的节点)的索引。这样,队列中始终是非空节点的索引。14.但是,这种方法依赖于输入数组是按照完全二叉树的方式(包括空节点)进层序遍历得到的。如果输入不是这样,则此方法不适用。15.用户的问题描述中并没有明确输入格式,所以我们假设输入是这种格式。如果用户有不同输入格式,可能需要调整。16.代码实现(Java):importjava.util.*;public classMain {publicstatic voidmain(String[] args) {Scannersc =new Scanner(System.in);//假设输入一整数,用空格分隔,包括-1表示空节点String[]nodes= sc.nextLine().split("");int n= nodes.length;if(n==0){System.out.println("");return;}int[] arr= newint[n];for (int i=0;i <n;i++){arr[i] =Integer.parseInt(nodes[i]);}//如果根节点为空,则输出空if(arr[0] ==-1) {System.out.println("");return;}Queue<Integer>queue =new LinkedList<>();//存储节点的索引queue.offer(0);//根节点索引List<String>levels =new ArrayList<>();//存储每一层的输出字符串while(!queue.isEmpty()){int size= queue.size();List<String> currentLevel =new ArrayList<>();for(inti =0; i< size; i++) {intindex =queue.poll();currentLevel.add(String.valueOf(arr[index])); //当前节点的值//左子节点索引intleft =2* index+1;if(left< n&& arr[left]!= -1){queue.offer(left);}//右子节点索引intright =2* index+2;if(right< n&& arr[right]!= -1){queue.offer(right);}}//将当前层收集的节点值列表用空格连接levels.add(String.join("", currentLevel));}//输出每一层,每层一for (String level: levels) {System.out.println(level);}}}17.但是,上述代码有一个问题:如果输入数组不是完全二叉树,即空节点后面可能还有非空节点?但是按照完全二叉树的层序遍历,空节点后面不应该有非空节点(除非是下一层的,但下一层的节点应该出现在数组后面)。所以,我们假设输入数组是严格按照完全二叉树的层序遍历(包括空节点)给出的。18.另一种情况:如果输入数组不是完全二叉树,但题目要求按层级遍历输出,那么我们需要构建二叉树节点,然后进标准的BFS。但是,题目输入中给出了空节点(-1),所以我们可以根据输入数组构建二叉树(构建成节点对象的形式),然后再进BFS。19.构建二叉树的方法(节点对象):我们可以递归构建,但是输入是层序数组,所以我们可以用队列来构建:a.创建一个队列用于构建二叉树b.将根节点(数组第一个元素)创建一个节点,加入队列c.然后依次从队列中取出节点,并为其分配左右子节点:下一个数组元素是左子节点(如果非-1则创建节点并挂到左孩子,然后加入队列;否则为null,不加入队列)再下一个数组元素是右子节点(同理)d.直到数组元素用完。20.然后,再进BFS(同样用队列),按层输出。21.两种方法都可以,但第一种方法更节省空间(不需要构建树节点对象)。这里我们采用第一种方法,因为它直接利用数组索引。22.但是,如果输入数组非常大,第一种方法可能会访问到很多无效的索引(比如,当前节点为空,但是它的左右子节点索引可能很大,但数组中没有那么多元素)。所以我们在访问左右子节点索引时,需要检查索引是否在数组范围内。23.我们按照第一种方法实现。24.测试输入:123-1-145期望输出:1234525.但是,注意:第二层的节点2和3,节点2的左右子节点都是-1,所以不加入队列;节点3的左右子节点是4和5(索引5和6,在数组范围内且不为-1),所以加入队列。26.输出格式:每输出一层,节点值之间用空格隔开,行尾空格。27.如果输入只有一个节点,例如 [1],则输出:128.如果输入为空,则输出空。29.考虑边界情况:输入数组为 [-1]则输出空。30.代码调整:在加入左右子节点时,我们只检查索引在数组范围内且值不为-1。31.但是,如果数组很大,但中间有很多-1,那么队列中不会加入这些-1节点的子节点,所以不会访问无效索引。32.实现代码如上。33.注意:这种方法要求输入数组是严格按照完全二叉树的层序(包括空节点)给出的。如果用户输入不是这样(例如,省略了后面的空节点),则可能出错。但题目没有明确,我们就按常见情况处理。34.如果题目要求输入是前序遍历等,则用户需要说明,但问题描述是按照层级遍历输出,所以输入应该是层序数组。35.输出:每层一,每节点值用空格隔开。36.如果用户要求输出每层节点值,但不要分层的(即全部在一输出)?但是问题要求“输出每层节点值”,通常每层一。所以按照每一层输出。37.如果用户希望输出在一,用空格隔开,不分层,那么问题描述就不一样了。这里我们按照分层输出。38.现在,我们按照上述思路编写代码。39.但是,用户要求“去除空节点-1”,所以我们在遍历过程中不会将空节点加入队列,因此输出中不会出现-1。40.代码已实现。41.如果用户提供的输入不是完全二叉树的层序数组(即省略了最后的空节点),那么我们的方法可能无法正确构建。例如,输入数组为[1,2,3,4](省略了节点3的右子节点和节点2的左右子节点后面的空节点),那么按照完全二叉树的规则:节点0(1)的左子节点为1(2),右子节点为2(3)节点1(2)的左子节点为3(4),右子节点为4(但数组长度为4,索引4越界)所以,我们只加入节点1的左子节点(索引3,值为4),而右子节点索引4超出数组范围,不加入。这样,第二层输出2和3,第三层输出4。符合预期。42.因此,即使输入省略了最后的空节点,只要数组是按照层序排列的,并且每个非空节点的左右子节点在数组中对应位置(如果存在)正确,我们的方法就有效。43.所以,我们采用第一种方法。44.代码实现(Java)如上。45.如果用户使用其他语言,请根据需要进转换。46.注意:在输出时,我们每层收集节点值,然后拼接成字符串输出。这样避免了行尾空格。47.相关问题:§§相关问题§§1.如果输入不是完全二叉树的层序数组,而是前序遍历数组,如何构建二叉树并进层序遍历?2.如何计算二叉树的高度(深度)?3.除了层序遍历,还有哪些遍历二叉树的方式?4.如何判断一个二叉树是完全二叉树?</think>### 二叉树层级遍历输出解决方案 #### 核心思路 要实现二叉树层级遍历输出每层节点值(去除空节点),并确保行尾无多余空格,需遵循以下步骤: 1. **层级遍历(BFS)**:使用队列实现广度优先搜索,按层处理节点 2. **空节点处理**:遇到空节点(-1)时跳过不入队 3. **分层输出**:记录每层节点数,单独收集每层结果 4. **格式控制**:每层节点值用空格连接,确保尾无空格 #### Java实现代码 ```java import java.util.*; public class LevelOrderTraversal { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 读取节点数 int n = sc.nextInt(); int[] tree = new int[n]; // 存储二叉树节点值 for (int i = 0; i < n; i++) { tree[i] = sc.nextInt(); } // 存储最终结果 List<String> levels = new ArrayList<>(); Queue<Integer> queue = new LinkedList<>(); // 根节点入队(索引0) if (n > 0 && tree[0] != -1) { queue.offer(0); } while (!queue.isEmpty()) { int levelSize = queue.size(); List<Integer> currentLevel = new ArrayList<>(); // 处理当前层 for (int i = 0; i < levelSize; i++) { int index = queue.poll(); currentLevel.add(tree[index]); // 收集非空节点 // 计算左右子节点索引 int left = 2 * index + 1; int right = 2 * index + 2; // 左子节点入队(非空时) if (left < n && tree[left] != -1) { queue.offer(left); } // 右子节点入队(非空时) if (right < n && tree[right] != -1) { queue.offer(right); } } // 将当前层节点转为字符串(空格分隔) StringBuilder sb = new StringBuilder(); for (int val : currentLevel) { sb.append(val).append(" "); } // 删除末尾空格后存入结果 if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); levels.add(sb.toString()); } } // 输出结果(每层一) for (String level : levels) { System.out.println(level); } } } ``` #### 关键点说明 1. **输入格式**:假设输入为完全二叉树的层序序列,空节点用 `-1` 表示 - 示例输入:`7 [1,2,3,4,-1,5,-1]` 表示: ``` 1 / \ 2 3 / / 4 5 ``` 2. **队列操作**: - 根节点索引 `0` 先入队 - 节点出队时,将其**非空子节点**的索引入队 3. **分层控制**: - `levelSize` 记录每层节点数 - `currentLevel` 收集当前层有效节点 4. **空格处理**: - 使用 `StringBuilder` 构建每层输出 - `deleteCharAt()` 确保行尾空格 #### 示例运 **输入**: ``` 7 1 2 3 4 -1 5 -1 ``` **输出**: ``` 1 2 3 4 5 ``` #### 算法复杂度 - **时间复杂度**:$O(N)$,每个节点处理一次 - **空间复杂度**:$O(N)$,队列和结果存储空间 此方案严格满足: - 去除空节点(-1) - 每层节点值空格分隔 - 行尾无多余空格 - 每层单独一输出 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值